[USRMGR]
[reactos.git] / reactos / dll / cpl / usrmgr / userprops.c
index 736c83d..a9cd031 100644 (file)
 /*
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS User Manager Control Panel
- * FILE:            dll/cpl/usrmgr/users.c
- * PURPOSE:         Users property page
+ * FILE:            dll/cpl/usrmgr/userprops.c
+ * PURPOSE:         User property sheet
  *
  * PROGRAMMERS:     Eric Kohl
  */
 
 #include "usrmgr.h"
 
+typedef struct _GENERAL_USER_DATA
+{
+    DWORD dwFlags;
+    DWORD dwPasswordExpired;
+    TCHAR szUserName[1];
+} GENERAL_USER_DATA, *PGENERAL_USER_DATA;
+
+#define VALID_GENERAL_FLAGS (UF_PASSWD_CANT_CHANGE | UF_DONT_EXPIRE_PASSWD | UF_ACCOUNTDISABLE | UF_LOCKOUT)
+
+typedef struct _PROFILE_USER_DATA
+{
+    TCHAR szUserName[1];
+} PROFILE_USER_DATA, *PPROFILE_USER_DATA;
+
+typedef struct _MEMBERSHIP_USER_DATA
+{
+    PLOCALGROUP_USERS_INFO_0 pGroupData;
+    DWORD dwGroupCount;
+    TCHAR szUserName[1];
+} MEMBERSHIP_USER_DATA, *PMEMBERSHIP_USER_DATA;
+
+
+static VOID
+GetUserProfileData(HWND hwndDlg,
+                   PPROFILE_USER_DATA pUserData)
+{
+    PUSER_INFO_3 userInfo = NULL;
+    NET_API_STATUS status;
+    BOOL bLocal;
+    TCHAR szDrive[8];
+    INT i;
+    INT nSel;
+
+    status = NetUserGetInfo(NULL, pUserData->szUserName, 3, (LPBYTE*)&userInfo);
+    if (status != NERR_Success)
+        return;
+
+    SetDlgItemText(hwndDlg, IDC_USER_PROFILE_PATH, userInfo->usri3_profile);
+    SetDlgItemText(hwndDlg, IDC_USER_PROFILE_SCRIPT, userInfo->usri3_script_path);
+
+
+    bLocal = (userInfo->usri3_home_dir_drive == NULL) ||
+              (_tcslen(userInfo->usri3_home_dir_drive) == 0);
+    CheckRadioButton(hwndDlg, IDC_USER_PROFILE_LOCAL, IDC_USER_PROFILE_REMOTE,
+                     bLocal ? IDC_USER_PROFILE_LOCAL : IDC_USER_PROFILE_REMOTE);
+
+    for (i = 0; i < 26; i++)
+    {
+        wsprintf(szDrive, _T("%c:"), (TCHAR)('A' + i));
+        nSel = SendMessage(GetDlgItem(hwndDlg, IDC_USER_PROFILE_DRIVE),
+                           CB_INSERTSTRING, -1, (LPARAM)szDrive);
+    }
+
+    if (bLocal)
+    {
+        SetDlgItemText(hwndDlg, IDC_USER_PROFILE_LOCAL_PATH, userInfo->usri3_home_dir);
+        EnableWindow(GetDlgItem(hwndDlg, IDC_USER_PROFILE_DRIVE), FALSE);
+        EnableWindow(GetDlgItem(hwndDlg, IDC_USER_PROFILE_REMOTE_PATH), FALSE);
+    }
+    else
+    {
+        SetDlgItemText(hwndDlg, IDC_USER_PROFILE_REMOTE_PATH, userInfo->usri3_home_dir);
+        nSel = SendMessage(GetDlgItem(hwndDlg, IDC_USER_PROFILE_DRIVE),
+                           CB_FINDSTRINGEXACT, -1, (LPARAM)userInfo->usri3_home_dir_drive);
+    }
+
+    SendMessage(GetDlgItem(hwndDlg, IDC_USER_PROFILE_DRIVE),
+                CB_SETCURSEL, nSel, 0);
+
+    NetApiBufferFree(userInfo);
+}
+
+
+static BOOL
+SetUserProfileData(HWND hwndDlg,
+                   PPROFILE_USER_DATA pUserData)
+{
+    PUSER_INFO_3 pUserInfo = NULL;
+    LPTSTR pszProfilePath = NULL;
+    LPTSTR pszScriptPath = NULL;
+    LPTSTR pszHomeDir = NULL;
+    LPTSTR pszHomeDrive = NULL;
+    NET_API_STATUS status;
+#if 0
+    DWORD dwIndex;
+#endif
+    INT nLength;
+    INT nIndex;
+
+    NetUserGetInfo(NULL, pUserData->szUserName, 3, (LPBYTE*)&pUserInfo);
+
+    /* Get the profile path */
+    nLength = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_USER_PROFILE_PATH));
+    if (nLength == 0)
+    {
+        pUserInfo->usri3_profile = NULL;
+    }
+    else
+    {
+        pszProfilePath = HeapAlloc(GetProcessHeap(), 0, (nLength + 1) * sizeof(TCHAR));
+        GetDlgItemText(hwndDlg, IDC_USER_PROFILE_PATH, pszProfilePath, nLength + 1);
+        pUserInfo->usri3_profile = pszProfilePath;
+    }
+
+    /* Get the script path */
+    nLength = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_USER_PROFILE_SCRIPT));
+    if (nLength == 0)
+    {
+        pUserInfo->usri3_script_path = NULL;
+    }
+    else
+    {
+        pszScriptPath = HeapAlloc(GetProcessHeap(), 0, (nLength + 1) * sizeof(TCHAR));
+        GetDlgItemText(hwndDlg, IDC_USER_PROFILE_SCRIPT, pszScriptPath, nLength + 1);
+        pUserInfo->usri3_script_path = pszScriptPath;
+    }
+
+    if (IsDlgButtonChecked(hwndDlg, IDC_USER_PROFILE_LOCAL) == BST_CHECKED)
+    {
+        /* Local home directory */
+        nLength = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_USER_PROFILE_LOCAL_PATH));
+        if (nLength == 0)
+        {
+            pUserInfo->usri3_home_dir = NULL;
+        }
+        else
+        {
+            pszHomeDir = HeapAlloc(GetProcessHeap(), 0, (nLength + 1) * sizeof(TCHAR));
+            GetDlgItemText(hwndDlg, IDC_USER_PROFILE_LOCAL_PATH, pszHomeDir, nLength + 1);
+            pUserInfo->usri3_home_dir = pszHomeDir;
+        }
+    }
+    else
+    {
+        /* Remote home directory */
+        nLength = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_USER_PROFILE_REMOTE_PATH));
+        if (nLength == 0)
+        {
+            pUserInfo->usri3_home_dir = NULL;
+        }
+        else
+        {
+            pszHomeDir = HeapAlloc(GetProcessHeap(), 0, (nLength + 1) * sizeof(TCHAR));
+            GetDlgItemText(hwndDlg, IDC_USER_PROFILE_REMOTE_PATH, pszHomeDir, nLength + 1);
+            pUserInfo->usri3_home_dir = pszHomeDir;
+        }
+
+        nIndex = SendMessage(GetDlgItem(hwndDlg, IDC_USER_PROFILE_DRIVE), CB_GETCURSEL, 0, 0);
+        if (nIndex != CB_ERR)
+        {
+            nLength = SendMessage(GetDlgItem(hwndDlg, IDC_USER_PROFILE_DRIVE), CB_GETLBTEXTLEN, nIndex, 0);
+            pszHomeDrive = HeapAlloc(GetProcessHeap(), 0, (nLength + 1) * sizeof(TCHAR));
+            SendMessage(GetDlgItem(hwndDlg, IDC_USER_PROFILE_DRIVE), CB_GETLBTEXT, nIndex, (LPARAM)pszHomeDrive);
+            pUserInfo->usri3_home_dir_drive = pszHomeDrive;
+        }
+    }
+
+#if 0
+    status = NetUserSetInfo(NULL, pUserData->szUserName, 3, (LPBYTE)pUserInfo, &dwIndex);
+    if (status != NERR_Success)
+    {
+        DebugPrintf(_T("Status: %lu  Index: %lu"), status, dwIndex);
+    }
+#else
+    status = NERR_Success;
+#endif
+
+    if (pszProfilePath)
+        HeapFree(GetProcessHeap(), 0, pszProfilePath);
+
+    if (pszScriptPath)
+        HeapFree(GetProcessHeap(), 0, pszScriptPath);
+
+    if (pszHomeDir)
+        HeapFree(GetProcessHeap(), 0, pszHomeDir);
+
+    if (pszHomeDrive)
+        HeapFree(GetProcessHeap(), 0, pszHomeDrive);
+
+    NetApiBufferFree(pUserInfo);
+
+    return (status == NERR_Success);
+}
+
+
+INT_PTR CALLBACK
+UserProfilePageProc(HWND hwndDlg,
+                    UINT uMsg,
+                    WPARAM wParam,
+                    LPARAM lParam)
+{
+    PPROFILE_USER_DATA pUserData;
+
+    UNREFERENCED_PARAMETER(lParam);
+    UNREFERENCED_PARAMETER(wParam);
+    UNREFERENCED_PARAMETER(hwndDlg);
+
+    pUserData= (PPROFILE_USER_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
+
+    switch (uMsg)
+    {
+        case WM_INITDIALOG:
+            pUserData = (PPROFILE_USER_DATA)HeapAlloc(GetProcessHeap(),
+                                                      HEAP_ZERO_MEMORY,
+                                                      sizeof(PROFILE_USER_DATA) + 
+                                                      lstrlen((LPTSTR)((PROPSHEETPAGE *)lParam)->lParam) * sizeof(TCHAR));
+            lstrcpy(pUserData->szUserName, (LPTSTR)((PROPSHEETPAGE *)lParam)->lParam);
+
+            SetWindowLongPtr(hwndDlg, DWLP_USER, (INT_PTR)pUserData);
+
+            GetUserProfileData(hwndDlg,
+                               pUserData);
+            break;
+
+        case WM_COMMAND:
+            switch (LOWORD(wParam))
+            {
+                case IDC_USER_PROFILE_PATH:
+                case IDC_USER_PROFILE_SCRIPT:
+                    if (HIWORD(wParam) == EN_CHANGE)
+                        PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
+                    break;
+
+                case IDC_USER_PROFILE_LOCAL:
+                    EnableWindow(GetDlgItem(hwndDlg, IDC_USER_PROFILE_LOCAL_PATH), TRUE);
+                    EnableWindow(GetDlgItem(hwndDlg, IDC_USER_PROFILE_DRIVE), FALSE);
+                    EnableWindow(GetDlgItem(hwndDlg, IDC_USER_PROFILE_REMOTE_PATH), FALSE);
+                    break;
+
+                case IDC_USER_PROFILE_REMOTE:
+                    EnableWindow(GetDlgItem(hwndDlg, IDC_USER_PROFILE_LOCAL_PATH), FALSE);
+                    EnableWindow(GetDlgItem(hwndDlg, IDC_USER_PROFILE_DRIVE), TRUE);
+                    EnableWindow(GetDlgItem(hwndDlg, IDC_USER_PROFILE_REMOTE_PATH), TRUE);
+                    break;
+            }
+            break;
+
+        case WM_DESTROY:
+            HeapFree(GetProcessHeap(), 0, pUserData);
+            break;
+
+        case WM_NOTIFY:
+            if (((LPPSHNOTIFY)lParam)->hdr.code == PSN_APPLY)
+            {
+                SetUserProfileData(hwndDlg, pUserData);
+                return TRUE;
+            }
+            break;
+    }
+
+    return FALSE;
+}
+
+
+static VOID
+GetUserMembershipData(HWND hwndDlg, PMEMBERSHIP_USER_DATA pUserData)
+{
+    NET_API_STATUS status;
+    DWORD dwTotal;
+    DWORD i;
+    HIMAGELIST hImgList;
+    HICON hIcon;
+    LV_ITEM lvi;
+    HWND hwndLV;
+    LV_COLUMN column;
+    RECT rect;
+
+
+    hwndLV = GetDlgItem(hwndDlg, IDC_USER_MEMBERSHIP_LIST);
+
+    /* Create the image list */
+    hImgList = ImageList_Create(16, 16, ILC_COLOR8 | ILC_MASK, 5, 5);
+    hIcon = LoadImage(hApplet, MAKEINTRESOURCE(IDI_GROUP), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
+    ImageList_AddIcon(hImgList, hIcon);
+    DestroyIcon(hIcon);
+    (void)ListView_SetImageList(hwndLV, hImgList, LVSIL_SMALL);
+
+    /* Set the list column */
+    GetClientRect(hwndLV, &rect);
+
+    memset(&column, 0x00, sizeof(column));
+    column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM;
+    column.fmt = LVCFMT_LEFT;
+    column.cx = (INT)(rect.right - rect.left);
+    column.iSubItem = 0;
+    (void)ListView_InsertColumn(hwndLV, 0, &column);
+
+
+    status = NetUserGetLocalGroups(NULL, pUserData->szUserName, 0, 0,
+                                   (LPBYTE*)&pUserData->pGroupData,
+                                   MAX_PREFERRED_LENGTH,
+                                   &pUserData->dwGroupCount,
+                                   &dwTotal);
+    if (status != NERR_Success)
+        return;
+
+    for (i = 0; i < pUserData->dwGroupCount; i++)
+    {
+        ZeroMemory(&lvi, sizeof(lvi));
+        lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE;
+        lvi.pszText = pUserData->pGroupData[i].lgrui0_name;
+        lvi.state = 0;
+        lvi.iImage = 0; 
+
+        (void)ListView_InsertItem(hwndLV, &lvi);
+    }
+}
+
+
+static VOID
+RemoveGroupFromUser(HWND hwndDlg,
+                    PMEMBERSHIP_USER_DATA pUserData)
+{
+    TCHAR szGroupName[UNLEN];
+    TCHAR szText[256];
+    LOCALGROUP_MEMBERS_INFO_3 memberInfo;
+    HWND hwndLV;
+    INT nItem;
+    NET_API_STATUS status;
+
+    hwndLV = GetDlgItem(hwndDlg, IDC_USER_MEMBERSHIP_LIST);
+    nItem = ListView_GetNextItem(hwndLV, -1, LVNI_SELECTED);
+    if (nItem == -1)
+        return;
+
+    /* Get the new user name */
+    ListView_GetItemText(hwndLV,
+                         nItem, 0,
+                         szGroupName,
+                         UNLEN);
+
+    /* Display a warning message because the remove operation cannot be reverted */
+    wsprintf(szText, TEXT("Do you really want to remove the user \"%s\" from the group \"%s\"?"),
+             pUserData->szUserName, szGroupName);
+    if (MessageBox(NULL, szText, TEXT("User Accounts"), MB_ICONWARNING | MB_YESNO) == IDNO)
+        return;
+
+    memberInfo.lgrmi3_domainandname = pUserData->szUserName;
+
+    status = NetLocalGroupDelMembers(NULL, szGroupName,
+                                     3, (LPBYTE)&memberInfo, 1);
+    if (status != NERR_Success)
+    {
+        TCHAR szText[256];
+        wsprintf(szText, TEXT("Error: %u"), status);
+        MessageBox(NULL, szText, TEXT("NetLocalGroupDelMembers"), MB_ICONERROR | MB_OK);
+        return;
+    }
+
+    (void)ListView_DeleteItem(hwndLV, nItem);
+
+    if (ListView_GetItemCount(hwndLV) == 0)
+        EnableWindow(GetDlgItem(hwndDlg, IDC_USER_MEMBERSHIP_REMOVE), FALSE);
+}
+
+
+static VOID
+InitUserGroupsList(HWND hwndDlg)
+{
+    HWND hwndLV;
+    LV_COLUMN column;
+    RECT rect;
+    TCHAR szStr[32];
+
+    NET_API_STATUS netStatus;
+    PLOCALGROUP_INFO_1 pBuffer;
+    DWORD entriesread;
+    DWORD totalentries;
+    DWORD_PTR resume_handle = 0;
+    DWORD i;
+    LV_ITEM lvi;
+    INT iItem;
+
+    HIMAGELIST hImgList;
+    HICON hIcon;
+
+    hwndLV = GetDlgItem(hwndDlg, IDC_USER_ADD_MEMBERSHIP_LIST);
+    GetClientRect(hwndLV, &rect);
+
+    hImgList = ImageList_Create(16,16,ILC_COLOR8 | ILC_MASK,5,5);
+    hIcon = LoadImage(hApplet,MAKEINTRESOURCE(IDI_GROUP),IMAGE_ICON,16,16,LR_DEFAULTCOLOR);
+    ImageList_AddIcon(hImgList,hIcon);
+    DestroyIcon(hIcon);
+
+    (void)ListView_SetImageList(hwndLV, hImgList, LVSIL_SMALL);
+    (void)ListView_SetExtendedListViewStyle(hwndLV, LVS_EX_FULLROWSELECT);
+
+    memset(&column, 0x00, sizeof(column));
+    column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM | LVCF_TEXT;
+    column.fmt = LVCFMT_LEFT;
+    column.cx = (INT)((rect.right - rect.left) * 0.40);
+    column.iSubItem = 0;
+    LoadString(hApplet, IDS_NAME, szStr, sizeof(szStr) / sizeof(szStr[0]));
+    column.pszText = szStr;
+    (void)ListView_InsertColumn(hwndLV, 0, &column);
+
+    column.cx = (INT)((rect.right - rect.left) * 0.60);
+    column.iSubItem = 1;
+    LoadString(hApplet, IDS_DESCRIPTION, szStr, sizeof(szStr) / sizeof(szStr[0]));
+    column.pszText = szStr;
+    (void)ListView_InsertColumn(hwndLV, 1, &column);
+
+    for (;;)
+    {
+        netStatus = NetLocalGroupEnum(NULL, 1, (LPBYTE*)&pBuffer,
+                                      1024, &entriesread,
+                                      &totalentries, &resume_handle);
+        if (netStatus != NERR_Success && netStatus != ERROR_MORE_DATA)
+            break;
+
+        for (i = 0; i < entriesread; i++)
+        {
+           memset(&lvi, 0x00, sizeof(lvi));
+           lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE;
+           lvi.pszText = pBuffer[i].lgrpi1_name;
+           lvi.state = 0;
+           lvi.iImage = 0;
+           iItem = ListView_InsertItem(hwndLV, &lvi);
+
+           ListView_SetItemText(hwndLV, iItem, 1,
+                                pBuffer[i].lgrpi1_comment);
+        }
+
+        NetApiBufferFree(pBuffer);
+
+        /* No more data left */
+        if (netStatus != ERROR_MORE_DATA)
+            break;
+    }
+}
+
+
+static BOOL
+AddSelectedGroupsToUser(HWND hwndDlg,
+                        PMEMBERSHIP_USER_DATA pUserData)
+{
+    HWND hwndLV;
+    INT nItem;
+    TCHAR szGroupName[UNLEN];
+    BOOL bResult = FALSE;
+    BOOL bFound;
+    DWORD i;
+    LOCALGROUP_MEMBERS_INFO_3 memberInfo;
+    NET_API_STATUS status;
+
+    hwndLV = GetDlgItem(hwndDlg, IDC_USER_ADD_MEMBERSHIP_LIST);
+
+    if (ListView_GetSelectedCount(hwndLV) > 0)
+    {
+        nItem = ListView_GetNextItem(hwndLV, -1, LVNI_SELECTED);
+        while (nItem != -1)
+        {
+            /* Get the new user name */
+            ListView_GetItemText(hwndLV,
+                                 nItem, 0,
+                                 szGroupName,
+                                 UNLEN);
+
+            bFound = FALSE;
+            for (i = 0; i < pUserData->dwGroupCount; i++)
+            {
+                if (_tcscmp(pUserData->pGroupData[i].lgrui0_name, szGroupName) == 0)
+                    bFound = TRUE;
+            }
+
+            if (!bFound)
+            {
+                memberInfo.lgrmi3_domainandname = pUserData->szUserName;
+
+                status = NetLocalGroupAddMembers(NULL, szGroupName, 3,
+                                                 (LPBYTE)&memberInfo, 1);
+                if (status == NERR_Success)
+                {
+                    DebugPrintf(_TEXT("Selected group: %s"), szGroupName);
+                    bResult = TRUE;
+                }
+                else
+                {
+                    TCHAR szText[256];
+                    wsprintf(szText, TEXT("Error: %u"), status);
+                    MessageBox(NULL, szText, TEXT("NetLocalGroupAddMembers"), MB_ICONERROR | MB_OK);
+                }
+            }
+
+            nItem = ListView_GetNextItem(hwndLV, nItem, LVNI_SELECTED);
+        }
+    }
+
+    return bResult;
+}
+
+
+INT_PTR CALLBACK
+AddGroupToUserDlgProc(HWND hwndDlg,
+                      UINT uMsg,
+                      WPARAM wParam,
+                      LPARAM lParam)
+{
+    PMEMBERSHIP_USER_DATA pUserData;
+
+    UNREFERENCED_PARAMETER(wParam);
+
+    pUserData= (PMEMBERSHIP_USER_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
+
+    switch (uMsg)
+    {
+        case WM_INITDIALOG:
+            pUserData= (PMEMBERSHIP_USER_DATA)lParam;
+            SetWindowLongPtr(hwndDlg, DWLP_USER, (INT_PTR)pUserData);
+            InitUserGroupsList(hwndDlg);
+            break;
+
+        case WM_COMMAND:
+            switch (LOWORD(wParam))
+            {
+                case IDOK:
+                    if (AddSelectedGroupsToUser(hwndDlg, pUserData))
+                        EndDialog(hwndDlg, IDOK);
+                    else
+                        EndDialog(hwndDlg, IDCANCEL);
+                    break;
+
+                case IDCANCEL:
+                    EndDialog(hwndDlg, IDCANCEL);
+                    break;
+            }
+            break;
+
+        default:
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
+
+static VOID
+AddGroupToUser(HWND hwndDlg,
+               PMEMBERSHIP_USER_DATA pUserData)
+{
+    HWND hwndLV;
+    NET_API_STATUS status;
+    DWORD i;
+    DWORD dwTotal;
+    LV_ITEM lvi;
+
+    if (DialogBoxParam(hApplet,
+                       MAKEINTRESOURCE(IDD_USER_ADD_MEMBERSHIP),
+                       hwndDlg,
+                       AddGroupToUserDlgProc,
+                       (LPARAM)pUserData) == IDOK)
+    {
+        // TODO: Update Membership list!
+        hwndLV = GetDlgItem(hwndDlg, IDC_USER_MEMBERSHIP_LIST);
+
+        if (pUserData->pGroupData)
+            NetApiBufferFree(pUserData->pGroupData);
+
+        (void)ListView_DeleteAllItems(hwndLV);
+
+        status = NetUserGetLocalGroups(NULL, pUserData->szUserName, 0, 0,
+                                       (LPBYTE*)&pUserData->pGroupData,
+                                       MAX_PREFERRED_LENGTH,
+                                       &pUserData->dwGroupCount,
+                                       &dwTotal);
+        if (status != NERR_Success)
+            return;
+
+        for (i = 0; i < pUserData->dwGroupCount; i++)
+        {
+            ZeroMemory(&lvi, sizeof(lvi));
+            lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE;
+            lvi.pszText = pUserData->pGroupData[i].lgrui0_name;
+            lvi.state = 0;
+            lvi.iImage = 0; 
+
+            (void)ListView_InsertItem(hwndLV, &lvi);
+        }
+    }
+}
+
+
+static BOOL
+OnNotify(HWND hwndDlg,
+         PMEMBERSHIP_USER_DATA pUserData,
+         LPARAM lParam)
+{
+    LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW)lParam;
+
+    switch (((LPNMHDR)lParam)->idFrom)
+    {
+        case IDC_USER_MEMBERSHIP_LIST:
+            switch (((LPNMHDR)lParam)->code)
+            {
+                case NM_CLICK:
+                    EnableWindow(GetDlgItem(hwndDlg, IDC_USER_MEMBERSHIP_REMOVE), (lpnmlv->iItem != -1));
+                    break;
+
+                case LVN_KEYDOWN:
+                    if (((LPNMLVKEYDOWN)lParam)->wVKey == VK_DELETE)
+                    {
+                        RemoveGroupFromUser(hwndDlg, pUserData);
+                    }
+                    break;
+
+            }
+            break;
+    }
+
+    return FALSE;
+}
+
+
+INT_PTR CALLBACK
+UserMembershipPageProc(HWND hwndDlg,
+                       UINT uMsg,
+                       WPARAM wParam,
+                       LPARAM lParam)
+{
+    PMEMBERSHIP_USER_DATA pUserData;
+
+    UNREFERENCED_PARAMETER(lParam);
+    UNREFERENCED_PARAMETER(wParam);
+    UNREFERENCED_PARAMETER(hwndDlg);
+
+    pUserData= (PMEMBERSHIP_USER_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
+
+    switch (uMsg)
+    {
+        case WM_INITDIALOG:
+            pUserData = (PMEMBERSHIP_USER_DATA)HeapAlloc(GetProcessHeap(),
+                                                         HEAP_ZERO_MEMORY,
+                                                         sizeof(MEMBERSHIP_USER_DATA) + 
+                                                         lstrlen((LPTSTR)((PROPSHEETPAGE *)lParam)->lParam) * sizeof(TCHAR));
+            lstrcpy(pUserData->szUserName, (LPTSTR)((PROPSHEETPAGE *)lParam)->lParam);
+
+            SetWindowLongPtr(hwndDlg, DWLP_USER, (INT_PTR)pUserData);
+
+            GetUserMembershipData(hwndDlg, pUserData);
+            break;
+
+        case WM_COMMAND:
+            switch (LOWORD(wParam))
+            {
+                case IDC_USER_MEMBERSHIP_ADD:
+                    AddGroupToUser(hwndDlg, pUserData);
+                    break;
+
+                case IDC_USER_MEMBERSHIP_REMOVE:
+                    RemoveGroupFromUser(hwndDlg, pUserData);
+                    break;
+            }
+            break;
+
+        case WM_NOTIFY:
+            if (((LPPSHNOTIFY)lParam)->hdr.code == PSN_APPLY)
+            {
+                return TRUE;
+            }
+            else
+            {
+                return OnNotify(hwndDlg, pUserData, lParam);
+            }
+            break;
+
+
+        case WM_DESTROY:
+            if (pUserData->pGroupData)
+                NetApiBufferFree(pUserData->pGroupData);
+
+            HeapFree(GetProcessHeap(), 0, pUserData);
+            break;
+    }
+
+    return FALSE;
+}
+
+
 static VOID
 UpdateUserOptions(HWND hwndDlg,
-                  PUSER_INFO_3 userInfo,
+                  PGENERAL_USER_DATA pUserData,
                   BOOL bInit)
 {
     EnableWindow(GetDlgItem(hwndDlg, IDC_USER_GENERAL_CANNOT_CHANGE),
-                 !userInfo->usri3_password_expired);
+                 !pUserData->dwPasswordExpired);
     EnableWindow(GetDlgItem(hwndDlg, IDC_USER_GENERAL_NEVER_EXPIRES),
-                 !userInfo->usri3_password_expired);
+                 !pUserData->dwPasswordExpired);
     EnableWindow(GetDlgItem(hwndDlg, IDC_USER_GENERAL_FORCE_CHANGE),
-                 (userInfo->usri3_flags & (UF_PASSWD_CANT_CHANGE | UF_DONT_EXPIRE_PASSWD)) == 0);
+                 (pUserData->dwFlags & (UF_PASSWD_CANT_CHANGE | UF_DONT_EXPIRE_PASSWD)) == 0);
+
+    EnableWindow(GetDlgItem(hwndDlg, IDC_USER_GENERAL_LOCKED),
+                 (pUserData->dwFlags & UF_LOCKOUT) != 0);
 
     if (bInit)
     {
         CheckDlgButton(hwndDlg, IDC_USER_GENERAL_FORCE_CHANGE,
-                       userInfo->usri3_password_expired ? BST_CHECKED : BST_UNCHECKED);
+                       pUserData->dwPasswordExpired ? BST_CHECKED : BST_UNCHECKED);
 
         CheckDlgButton(hwndDlg, IDC_USER_GENERAL_CANNOT_CHANGE,
-                       (userInfo->usri3_flags & UF_PASSWD_CANT_CHANGE) ? BST_CHECKED : BST_UNCHECKED);
+                       (pUserData->dwFlags & UF_PASSWD_CANT_CHANGE) ? BST_CHECKED : BST_UNCHECKED);
 
         CheckDlgButton(hwndDlg, IDC_USER_GENERAL_NEVER_EXPIRES,
-                       (userInfo->usri3_flags & UF_DONT_EXPIRE_PASSWD) ? BST_CHECKED : BST_UNCHECKED);
+                       (pUserData->dwFlags & UF_DONT_EXPIRE_PASSWD) ? BST_CHECKED : BST_UNCHECKED);
 
         CheckDlgButton(hwndDlg, IDC_USER_GENERAL_DISABLED,
-                       (userInfo->usri3_flags & UF_ACCOUNTDISABLE) ? BST_CHECKED : BST_UNCHECKED);
+                       (pUserData->dwFlags & UF_ACCOUNTDISABLE) ? BST_CHECKED : BST_UNCHECKED);
+
+        CheckDlgButton(hwndDlg, IDC_USER_GENERAL_LOCKED,
+                       (pUserData->dwFlags & UF_LOCKOUT) ? BST_CHECKED : BST_UNCHECKED);
     }
 }
 
 
 static VOID
-GetUserData(HWND hwndDlg, LPTSTR lpUserName, PUSER_INFO_3 *usrInfo)
+GetUserGeneralData(HWND hwndDlg,
+                   PGENERAL_USER_DATA pUserData)
 {
-    PUSER_INFO_3 userInfo = NULL;
+    PUSER_INFO_3 pUserInfo = NULL;
 
-    SetDlgItemText(hwndDlg, IDC_USER_GENERAL_NAME, lpUserName);
+    SetDlgItemText(hwndDlg, IDC_USER_GENERAL_NAME, pUserData->szUserName);
 
-    NetUserGetInfo(NULL, lpUserName, 3, (LPBYTE*)&userInfo);
+    NetUserGetInfo(NULL, pUserData->szUserName, 3, (LPBYTE*)&pUserInfo);
 
-    SetDlgItemText(hwndDlg, IDC_USER_GENERAL_FULL_NAME, userInfo->usri3_full_name);
-    SetDlgItemText(hwndDlg, IDC_USER_GENERAL_DESCRIPTION, userInfo->usri3_comment);
+    SetDlgItemText(hwndDlg, IDC_USER_GENERAL_FULL_NAME, pUserInfo->usri3_full_name);
+    SetDlgItemText(hwndDlg, IDC_USER_GENERAL_DESCRIPTION, pUserInfo->usri3_comment);
 
-    UpdateUserOptions(hwndDlg, userInfo, TRUE);
+    pUserData->dwFlags = pUserInfo->usri3_flags;
+    pUserData->dwPasswordExpired = pUserInfo->usri3_password_expired;
 
-    *usrInfo = userInfo;
+    NetApiBufferFree(pUserInfo);
+
+    UpdateUserOptions(hwndDlg, pUserData, TRUE);
+}
+
+
+static BOOL
+SetUserGeneralData(HWND hwndDlg,
+                   PGENERAL_USER_DATA pUserData)
+{
+    PUSER_INFO_3 pUserInfo = NULL;
+    LPTSTR pszFullName = NULL;
+    LPTSTR pszComment = NULL;
+    NET_API_STATUS status;
+#if 0
+    DWORD dwIndex;
+#endif
+    INT nLength;
+
+    NetUserGetInfo(NULL, pUserData->szUserName, 3, (LPBYTE*)&pUserInfo);
+
+    pUserInfo->usri3_flags =
+        (pUserData->dwFlags & VALID_GENERAL_FLAGS) |
+        (pUserInfo->usri3_flags & ~VALID_GENERAL_FLAGS);
+
+    pUserInfo->usri3_password_expired = pUserData->dwPasswordExpired;
+
+    nLength = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_USER_GENERAL_FULL_NAME));
+    if (nLength == 0)
+    {
+        pUserInfo->usri3_full_name = NULL;
+    }
+    else
+    {
+        pszFullName = HeapAlloc(GetProcessHeap(), 0, (nLength + 1) * sizeof(TCHAR));
+        GetDlgItemText(hwndDlg, IDC_USER_GENERAL_FULL_NAME, pszFullName, nLength + 1);
+        pUserInfo->usri3_full_name = pszFullName;
+    }
+
+    nLength = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_USER_GENERAL_DESCRIPTION));
+    if (nLength == 0)
+    {
+        pUserInfo->usri3_full_name = NULL;
+    }
+    else
+    {
+        pszComment = HeapAlloc(GetProcessHeap(), 0, (nLength + 1) * sizeof(TCHAR));
+        GetDlgItemText(hwndDlg, IDC_USER_GENERAL_DESCRIPTION, pszComment, nLength + 1);
+        pUserInfo->usri3_comment = pszComment;
+    }
+
+#if 0
+    status = NetUserSetInfo(NULL, pUserData->szUserName, 3, (LPBYTE)pUserInfo, &dwIndex);
+    if (status != NERR_Success)
+    {
+        DebugPrintf(_T("Status: %lu  Index: %lu"), status, dwIndex);
+    }
+#else
+    status = NERR_Success;
+#endif
+
+    if (pszFullName)
+        HeapFree(GetProcessHeap(), 0, pszFullName);
+
+    if (pszComment)
+        HeapFree(GetProcessHeap(), 0, pszComment);
+
+    NetApiBufferFree(pUserInfo);
+
+    return (status == NERR_Success);
 }
 
 
@@ -62,52 +817,78 @@ UserGeneralPageProc(HWND hwndDlg,
                     WPARAM wParam,
                     LPARAM lParam)
 {
-    PUSER_INFO_3 userInfo;
+    PGENERAL_USER_DATA pUserData;
 
     UNREFERENCED_PARAMETER(lParam);
     UNREFERENCED_PARAMETER(wParam);
     UNREFERENCED_PARAMETER(hwndDlg);
 
-    userInfo = (PUSER_INFO_3)GetWindowLongPtr(hwndDlg, DWLP_USER);
+    pUserData= (PGENERAL_USER_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
 
     switch (uMsg)
     {
         case WM_INITDIALOG:
-            GetUserData(hwndDlg,
-                        (LPTSTR)((PROPSHEETPAGE *)lParam)->lParam,
-                        &userInfo);
-            SetWindowLongPtr(hwndDlg, DWLP_USER, (INT_PTR)userInfo);
+            pUserData = (PGENERAL_USER_DATA)HeapAlloc(GetProcessHeap(),
+                                                      HEAP_ZERO_MEMORY,
+                                                      sizeof(GENERAL_USER_DATA) + 
+                                                      lstrlen((LPTSTR)((PROPSHEETPAGE *)lParam)->lParam) * sizeof(TCHAR));
+            lstrcpy(pUserData->szUserName, (LPTSTR)((PROPSHEETPAGE *)lParam)->lParam);
+
+            SetWindowLongPtr(hwndDlg, DWLP_USER, (INT_PTR)pUserData);
+
+            GetUserGeneralData(hwndDlg,
+                               pUserData);
             break;
 
         case WM_COMMAND:
             switch (LOWORD(wParam))
             {
+                case IDC_USER_GENERAL_FULL_NAME:
+                case IDC_USER_GENERAL_DESCRIPTION:
+                    if (HIWORD(wParam) == EN_CHANGE)
+                        PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
+                    break;
+
                 case IDC_USER_GENERAL_FORCE_CHANGE:
-                    userInfo->usri3_password_expired = !userInfo->usri3_password_expired;
-                    UpdateUserOptions(hwndDlg, userInfo, FALSE);
+                    pUserData->dwPasswordExpired = !pUserData->dwPasswordExpired;
+                    UpdateUserOptions(hwndDlg, pUserData, FALSE);
+                    PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
                     break;
 
                 case IDC_USER_GENERAL_CANNOT_CHANGE:
-                    userInfo->usri3_flags ^= UF_PASSWD_CANT_CHANGE;
-                    UpdateUserOptions(hwndDlg, userInfo, FALSE);
+                    pUserData->dwFlags ^= UF_PASSWD_CANT_CHANGE;
+                    UpdateUserOptions(hwndDlg, pUserData, FALSE);
+                    PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
                     break;
 
                 case IDC_USER_GENERAL_NEVER_EXPIRES:
-                    userInfo->usri3_flags ^= UF_DONT_EXPIRE_PASSWD;
-                    UpdateUserOptions(hwndDlg, userInfo, FALSE);
+                    pUserData->dwFlags ^= UF_DONT_EXPIRE_PASSWD;
+                    UpdateUserOptions(hwndDlg, pUserData, FALSE);
+                    PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
                     break;
 
                 case IDC_USER_GENERAL_DISABLED:
-                    userInfo->usri3_flags ^= UF_ACCOUNTDISABLE;
+                    pUserData->dwFlags ^= UF_ACCOUNTDISABLE;
+                    PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
                     break;
 
                 case IDC_USER_GENERAL_LOCKED:
+                    pUserData->dwFlags ^= UF_LOCKOUT;
+                    PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
                     break;
             }
             break;
 
+        case WM_NOTIFY:
+            if (((LPPSHNOTIFY)lParam)->hdr.code == PSN_APPLY)
+            {
+                SetUserGeneralData(hwndDlg, pUserData);
+                return TRUE;
+            }
+            break;
+
         case WM_DESTROY:
-            NetApiBufferFree(userInfo);
+            HeapFree(GetProcessHeap(), 0, pUserData);
             break;
     }
 
@@ -128,10 +909,10 @@ InitPropSheetPage(PROPSHEETPAGE *psp, WORD idDlg, DLGPROC DlgProc, LPTSTR pszUse
 }
 
 
-VOID
+BOOL
 UserProperties(HWND hwndDlg)
 {
-    PROPSHEETPAGE psp[1];
+    PROPSHEETPAGE psp[3];
     PROPSHEETHEADER psh;
     TCHAR szUserName[UNLEN];
     INT nItem;
@@ -140,7 +921,7 @@ UserProperties(HWND hwndDlg)
     hwndLV = GetDlgItem(hwndDlg, IDC_USERS_LIST);
     nItem = ListView_GetNextItem(hwndLV, -1, LVNI_SELECTED);
     if (nItem == -1)
-        return;
+        return FALSE;
 
     /* Get the new user name */
     ListView_GetItemText(hwndLV,
@@ -160,8 +941,8 @@ UserProperties(HWND hwndDlg)
     psh.ppsp = psp;
 
     InitPropSheetPage(&psp[0], IDD_USER_GENERAL, (DLGPROC)UserGeneralPageProc, szUserName);
-//    InitPropSheetPage(&psp[1], IDD_USER_MEMBERSHIP, (DLGPROC)UserMembershipPageProc);
-//    InitPropSheetPage(&psp[2], IDD_USER_PROFILE, (DLGPROC)UserProfilePageProc);
+    InitPropSheetPage(&psp[1], IDD_USER_MEMBERSHIP, (DLGPROC)UserMembershipPageProc, szUserName);
+    InitPropSheetPage(&psp[2], IDD_USER_PROFILE, (DLGPROC)UserProfilePageProc, szUserName);
 
-    PropertySheet(&psh);
+    return (PropertySheet(&psh) == IDOK);
 }