xref: /reactos/dll/cpl/sysdm/userprofile.c (revision 58588b76)
1 /*
2  * PROJECT:     ReactOS System Control Panel Applet
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        dll/cpl/sysdm/userprofile.c
5  * PURPOSE:     Computer settings for networking
6  * COPYRIGHT:   Copyright Thomas Weidenmueller <w3seek@reactos.org>
7  *              Copyright 2006 Ged Murphy <gedmurphy@gmail.com>
8  *
9  */
10 
11 #include "precomp.h"
12 #include <sddl.h>
13 
14 #include <debug.h>
15 
16 typedef struct _PROFILEDATA
17 {
18     BOOL bMyProfile;
19     DWORD dwState;
20     PWSTR pszFullName;
21 } PROFILEDATA, *PPROFILEDATA;
22 
23 
24 static
25 BOOL
26 OnProfileTypeInit(
27     HWND hwndDlg,
28     PPROFILEDATA pProfileData)
29 {
30     PWSTR pszRawBuffer = NULL, pszCookedBuffer = NULL;
31     INT nLength;
32 
33     nLength = LoadStringW(hApplet, IDS_USERPROFILE_TYPE_TEXT, (PWSTR)&pszRawBuffer, 0);
34     pszRawBuffer = NULL;
35     if (nLength == 0)
36         return FALSE;
37 
38     pszRawBuffer = HeapAlloc(GetProcessHeap(), 0, (nLength + 1) * sizeof(WCHAR));
39     if (pszRawBuffer == NULL)
40         return FALSE;
41 
42     LoadStringW(hApplet, IDS_USERPROFILE_TYPE_TEXT, pszRawBuffer, nLength + 1);
43 
44     pszCookedBuffer = HeapAlloc(GetProcessHeap(), 0, (nLength + wcslen(pProfileData->pszFullName) + 1) * sizeof(WCHAR));
45     if (pszCookedBuffer == NULL)
46         goto done;
47 
48     swprintf(pszCookedBuffer, pszRawBuffer, pProfileData->pszFullName);
49 
50     /* Set the full text */
51     SetDlgItemText(hwndDlg, IDC_USERPROFILE_TYPE_TEXT, pszCookedBuffer);
52 
53     /* FIXME: Right now, we support local user profiles only! */
54     EnableWindow(GetDlgItem(hwndDlg, IDC_USERPROFILE_TYPE_ROAMING), FALSE);
55     Button_SetCheck(GetDlgItem(hwndDlg, IDC_USERPROFILE_TYPE_LOCAL), BST_CHECKED);
56     EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
57 
58 done:
59     if (pszCookedBuffer != NULL)
60         HeapFree(GetProcessHeap(), 0, pszCookedBuffer);
61 
62     if (pszRawBuffer != NULL)
63         HeapFree(GetProcessHeap(), 0, pszRawBuffer);
64 
65     return TRUE;
66 }
67 
68 
69 static
70 INT_PTR
71 CALLBACK
72 UserProfileTypeDlgProc(
73     _In_ HWND hwndDlg,
74     _In_ UINT uMsg,
75     _In_ WPARAM wParam,
76     _In_ LPARAM lParam)
77 {
78     switch (uMsg)
79     {
80         case WM_INITDIALOG:
81             OnProfileTypeInit(hwndDlg, (PPROFILEDATA)lParam);
82             return TRUE;
83 
84         case WM_DESTROY:
85             break;
86 
87         case WM_COMMAND:
88             switch (LOWORD(wParam))
89             {
90                 case IDOK:
91                 case IDCANCEL:
92                     EndDialog(hwndDlg,
93                               LOWORD(wParam));
94                     return TRUE;
95             }
96             break;
97     }
98 
99     return FALSE;
100 }
101 
102 
103 static
104 BOOL
105 ChangeUserProfileType(
106     _In_ HWND hwndDlg)
107 {
108     HWND hwndListView;
109     LVITEM Item;
110     INT iSelected;
111 
112     DPRINT("ChangeUserProfileType(%p)\n", hwndDlg);
113 
114     hwndListView = GetDlgItem(hwndDlg, IDC_USERPROFILE_LIST);
115     if (hwndListView == NULL)
116         return FALSE;
117 
118     iSelected = ListView_GetNextItem(hwndListView, -1, LVNI_SELECTED);
119     if (iSelected == -1)
120         return FALSE;
121 
122     ZeroMemory(&Item, sizeof(LVITEM));
123     Item.mask = LVIF_PARAM;
124     Item.iItem = iSelected;
125     Item.iSubItem = 0;
126     if (!ListView_GetItem(hwndListView, &Item))
127         return FALSE;
128 
129     if (Item.lParam == 0)
130         return FALSE;
131 
132     if (DialogBoxParam(hApplet,
133                        MAKEINTRESOURCE(IDD_USERPROFILE_TYPE),
134                        hwndDlg,
135                        UserProfileTypeDlgProc,
136                        (LPARAM)Item.lParam) == IDOK)
137     {
138         /* FIXME: Update the profile list view */
139         return TRUE;
140     }
141 
142     return FALSE;
143 }
144 
145 
146 static
147 BOOL
148 DeleteUserProfile(
149     _In_ HWND hwndDlg)
150 {
151     WCHAR szTitle[64], szRawText[128], szCookedText[256];
152     HWND hwndListView;
153     LVITEM Item;
154     INT iSelected;
155     PPROFILEDATA pProfileData;
156 
157     DPRINT("DeleteUserProfile()\n");
158 
159     hwndListView = GetDlgItem(hwndDlg, IDC_USERPROFILE_LIST);
160     if (hwndListView == NULL)
161         return FALSE;
162 
163     iSelected = ListView_GetNextItem(hwndListView, -1, LVNI_SELECTED);
164     if (iSelected == -1)
165         return FALSE;
166 
167     ZeroMemory(&Item, sizeof(LVITEM));
168     Item.mask = LVIF_PARAM;
169     Item.iItem = iSelected;
170     Item.iSubItem = 0;
171     if (!ListView_GetItem(hwndListView, &Item))
172         return FALSE;
173 
174     if (Item.lParam == 0)
175         return FALSE;
176 
177     pProfileData = (PPROFILEDATA)Item.lParam;
178     if (pProfileData->bMyProfile)
179         return FALSE;
180 
181     LoadStringW(hApplet, IDS_USERPROFILE_CONFIRM_DELETE_TITLE, szTitle, ARRAYSIZE(szTitle));
182     LoadStringW(hApplet, IDS_USERPROFILE_CONFIRM_DELETE, szRawText, ARRAYSIZE(szRawText));
183     swprintf(szCookedText, szRawText, pProfileData->pszFullName);
184 
185     if (MessageBoxW(hwndDlg,
186                     szCookedText,
187                     szTitle,
188                     MB_ICONQUESTION | MB_YESNO) == IDYES)
189     {
190         /* FIXME: Delete the profile here! */
191         return TRUE;
192     }
193 
194     return FALSE;
195 }
196 
197 
198 static
199 INT_PTR
200 CALLBACK
201 CopyUserProfileDlgProc(
202     _In_ HWND hwndDlg,
203     _In_ UINT uMsg,
204     _In_ WPARAM wParam,
205     _In_ LPARAM lParam)
206 {
207     switch (uMsg)
208     {
209         case WM_INITDIALOG:
210             return TRUE;
211 
212         case WM_DESTROY:
213             break;
214 
215         case WM_COMMAND:
216             switch (LOWORD(wParam))
217             {
218                 case IDOK:
219                 case IDCANCEL:
220                     EndDialog(hwndDlg,
221                               LOWORD(wParam));
222                     return TRUE;
223             }
224             break;
225     }
226 
227     return FALSE;
228 }
229 
230 
231 static
232 BOOL
233 CopyUserProfile(
234     _In_ HWND hwndDlg)
235 {
236     HWND hwndListView;
237     LVITEM Item;
238     INT iSelected;
239 
240     DPRINT("CopyUserProfile()\n");
241 
242     hwndListView = GetDlgItem(hwndDlg, IDC_USERPROFILE_LIST);
243     if (hwndListView == NULL)
244         return FALSE;
245 
246     iSelected = ListView_GetNextItem(hwndListView, -1, LVNI_SELECTED);
247     if (iSelected == -1)
248         return FALSE;
249 
250     ZeroMemory(&Item, sizeof(LVITEM));
251     Item.mask = LVIF_PARAM;
252     Item.iItem = iSelected;
253     Item.iSubItem = 0;
254     if (!ListView_GetItem(hwndListView, &Item))
255         return FALSE;
256 
257     if (Item.lParam == 0)
258         return FALSE;
259 
260     if (DialogBoxParam(hApplet,
261                        MAKEINTRESOURCE(IDD_USERPROFILE_COPY),
262                        hwndDlg,
263                        CopyUserProfileDlgProc,
264                        (LPARAM)Item.lParam) == IDOK)
265     {
266         /* FIXME: Update the profile list view */
267         return TRUE;
268     }
269 
270     return FALSE;
271 }
272 
273 
274 static VOID
275 SetListViewColumns(
276     _In_ HWND hwndListView)
277 {
278     LV_COLUMN column;
279     RECT rect;
280     TCHAR szStr[32];
281 
282     GetClientRect(hwndListView, &rect);
283 
284     SendMessage(hwndListView, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT);
285 
286     memset(&column, 0x00, sizeof(column));
287     column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM | LVCF_TEXT;
288     column.fmt = LVCFMT_LEFT;
289     column.cx = (INT)((rect.right - rect.left) * 0.40);
290     column.iSubItem = 0;
291     LoadString(hApplet, IDS_USERPROFILE_NAME, szStr, ARRAYSIZE(szStr));
292     column.pszText = szStr;
293     (void)ListView_InsertColumn(hwndListView, 0, &column);
294 
295     column.fmt = LVCFMT_RIGHT;
296     column.cx = (INT)((rect.right - rect.left) * 0.15);
297     column.iSubItem = 1;
298     LoadString(hApplet, IDS_USERPROFILE_SIZE, szStr, ARRAYSIZE(szStr));
299     column.pszText = szStr;
300     (void)ListView_InsertColumn(hwndListView, 1, &column);
301 
302     column.fmt = LVCFMT_LEFT;
303     column.cx = (INT)((rect.right - rect.left) * 0.15);
304     column.iSubItem = 2;
305     LoadString(hApplet, IDS_USERPROFILE_TYPE, szStr, ARRAYSIZE(szStr));
306     column.pszText = szStr;
307     (void)ListView_InsertColumn(hwndListView, 2, &column);
308 
309     column.fmt = LVCFMT_LEFT;
310     column.cx = (INT)((rect.right - rect.left) * 0.15);
311     column.iSubItem = 3;
312     LoadString(hApplet, IDS_USERPROFILE_STATUS, szStr, ARRAYSIZE(szStr));
313     column.pszText = szStr;
314     (void)ListView_InsertColumn(hwndListView, 3, &column);
315 
316     column.fmt = LVCFMT_LEFT;
317     column.cx = (INT)((rect.right - rect.left) * 0.15) - GetSystemMetrics(SM_CYHSCROLL);
318     column.iSubItem = 4;
319     LoadString(hApplet, IDS_USERPROFILE_MODIFIED, szStr, ARRAYSIZE(szStr));
320     column.pszText = szStr;
321     (void)ListView_InsertColumn(hwndListView, 4, &column);
322 }
323 
324 
325 static VOID
326 AddUserProfile(
327     _In_ HWND hwndListView,
328     _In_ LPTSTR lpProfileSid,
329     _In_ PSID pMySid,
330     _In_ HKEY hProfileKey)
331 {
332     PPROFILEDATA pProfileData = NULL;
333     WCHAR szAccountName[128], szDomainName[128];
334     WCHAR szNameBuffer[256];
335     SID_NAME_USE Use;
336     DWORD dwAccountNameSize, dwDomainNameSize;
337     DWORD dwProfileData, dwSize, dwType, dwState = 0;
338     PWSTR ptr;
339     PSID pSid = NULL;
340     INT nId, iItem;
341     LV_ITEM lvi;
342 
343     if (!ConvertStringSidToSid(lpProfileSid,
344                                &pSid))
345         return;
346 
347     dwAccountNameSize = ARRAYSIZE(szAccountName);
348     dwDomainNameSize = ARRAYSIZE(szDomainName);
349     if (!LookupAccountSidW(NULL,
350                            pSid,
351                            szAccountName,
352                            &dwAccountNameSize,
353                            szDomainName,
354                            &dwDomainNameSize,
355                            &Use))
356     {
357         /* Unknown account */
358         LoadStringW(hApplet, IDS_USERPROFILE_ACCOUNT_UNKNOWN, szNameBuffer, ARRAYSIZE(szNameBuffer));
359     }
360     else
361     {
362         /* Show only the user accounts */
363         if (Use != SidTypeUser)
364             goto done;
365 
366         if (szAccountName[0] == UNICODE_NULL)
367         {
368             /* Deleted account */
369             LoadStringW(hApplet, IDS_USERPROFILE_ACCOUNT_DELETED, szNameBuffer, ARRAYSIZE(szNameBuffer));
370         }
371         else
372         {
373             /* Normal account */
374             wsprintf(szNameBuffer, L"%s\\%s", szDomainName, szAccountName);
375         }
376     }
377 
378     /* Get the profile state value */
379     dwSize = sizeof(dwState);
380     if (RegQueryValueExW(hProfileKey,
381                          L"State",
382                          NULL,
383                          &dwType,
384                          (LPBYTE)&dwState,
385                          &dwSize) != ERROR_SUCCESS)
386     {
387         dwState = 0;
388     }
389 
390     /* Create and fill the profile data entry */
391     dwProfileData = sizeof(PROFILEDATA) +
392                     ((wcslen(szNameBuffer) + 1) * sizeof(WCHAR));
393     pProfileData = HeapAlloc(GetProcessHeap(),
394                              0,
395                              dwProfileData);
396     if (pProfileData == NULL)
397         goto done;
398 
399     pProfileData->bMyProfile = EqualSid(pMySid, pSid);
400     pProfileData->dwState = dwState;
401 
402     ptr = (PWSTR)((ULONG_PTR)pProfileData + sizeof(PROFILEDATA));
403     pProfileData->pszFullName = ptr;
404 
405     wcscpy(pProfileData->pszFullName, szNameBuffer);
406 
407     /* Add the profile and set its name */
408     memset(&lvi, 0x00, sizeof(lvi));
409     lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
410     lvi.pszText = pProfileData->pszFullName;
411     lvi.state = 0;
412     lvi.lParam = (LPARAM)pProfileData;
413     iItem = ListView_InsertItem(hwndListView, &lvi);
414 
415     /* Set the profile type */
416     if (dwState & 0x0001) // PROFILE_MANDATORY
417         nId = IDS_USERPROFILE_MANDATORY;
418     else if (dwState & 0x0010) // PROFILE_UPDATE_CENTRAL
419         nId = IDS_USERPROFILE_ROAMING;
420     else
421         nId = IDS_USERPROFILE_LOCAL;
422 
423     LoadStringW(hApplet, nId, szAccountName, ARRAYSIZE(szAccountName));
424 
425     ListView_SetItemText(hwndListView, iItem, 2, szAccountName);
426 
427 done:
428     if (pSid != NULL)
429         LocalFree(pSid);
430 }
431 
432 
433 static VOID
434 UpdateButtonState(
435     _In_ HWND hwndDlg,
436     _In_ HWND hwndListView)
437 {
438     LVITEM Item;
439     INT iSelected;
440     BOOL bMyProfile;
441 
442     iSelected = ListView_GetNextItem(hwndListView, -1, LVNI_SELECTED);
443     if (iSelected != -1)
444     {
445         Item.mask = LVIF_PARAM;
446         Item.iItem = iSelected;
447         Item.iSubItem = 0;
448         if (ListView_GetItem(hwndListView, &Item))
449         {
450             if (Item.lParam != 0)
451             {
452                 bMyProfile = ((PPROFILEDATA)Item.lParam)->bMyProfile;
453                 if (/*IsUserAnAdmin() &&*/ !bMyProfile)
454                 {
455                     EnableWindow(GetDlgItem(hwndDlg, IDC_USERPROFILE_DELETE), TRUE);
456                     EnableWindow(GetDlgItem(hwndDlg, IDC_USERPROFILE_COPY), TRUE);
457                 }
458             }
459         }
460         EnableWindow(GetDlgItem(hwndDlg, IDC_USERPROFILE_CHANGE), TRUE);
461     }
462     else
463     {
464         EnableWindow(GetDlgItem(hwndDlg, IDC_USERPROFILE_CHANGE), FALSE);
465         EnableWindow(GetDlgItem(hwndDlg, IDC_USERPROFILE_DELETE), FALSE);
466         EnableWindow(GetDlgItem(hwndDlg, IDC_USERPROFILE_COPY), FALSE);
467     }
468 }
469 
470 
471 static VOID
472 AddUserProfiles(
473     _In_ HWND hwndDlg,
474     _In_ HWND hwndListView)
475 {
476     HKEY hKeyUserProfiles = INVALID_HANDLE_VALUE;
477     HKEY hProfileKey;
478     DWORD dwIndex;
479     WCHAR szProfileSid[64];
480     DWORD dwSidLength;
481     FILETIME ftLastWrite;
482     DWORD dwSize;
483     HANDLE hToken = NULL;
484     PTOKEN_USER pTokenUser = NULL;
485 
486     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
487         return;
488 
489     GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize);
490     if (dwSize == 0)
491         goto done;
492 
493     pTokenUser = HeapAlloc(GetProcessHeap(), 0, dwSize);
494     if (pTokenUser == NULL)
495         goto done;
496 
497     if (!GetTokenInformation(hToken, TokenUser, pTokenUser, dwSize, &dwSize))
498         goto done;
499 
500     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
501                       L"Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList",
502                       0,
503                       KEY_READ,
504                       &hKeyUserProfiles))
505         goto done;
506 
507     for (dwIndex = 0; ; dwIndex++)
508     {
509         dwSidLength = ARRAYSIZE(szProfileSid);
510         if (RegEnumKeyExW(hKeyUserProfiles,
511                           dwIndex,
512                           szProfileSid,
513                           &dwSidLength,
514                           NULL,
515                           NULL,
516                           NULL,
517                           &ftLastWrite))
518             break;
519 
520         if (RegOpenKeyExW(hKeyUserProfiles,
521                           szProfileSid,
522                           0,
523                           KEY_READ,
524                           &hProfileKey) == ERROR_SUCCESS)
525         {
526             AddUserProfile(hwndListView, szProfileSid, pTokenUser->User.Sid, hProfileKey);
527             RegCloseKey(hProfileKey);
528         }
529     }
530 
531     if (ListView_GetItemCount(hwndListView) != 0)
532         ListView_SetItemState(hwndListView, 0, LVIS_SELECTED, LVIS_SELECTED);
533 
534     UpdateButtonState(hwndDlg, hwndListView);
535 
536 done:
537     if (hKeyUserProfiles != INVALID_HANDLE_VALUE)
538         RegCloseKey(hKeyUserProfiles);
539 
540     if (pTokenUser != NULL)
541         HeapFree(GetProcessHeap(), 0, pTokenUser);
542 
543     if (hToken != NULL)
544         CloseHandle(hToken);
545 }
546 
547 
548 static VOID
549 OnInitUserProfileDialog(HWND hwndDlg)
550 {
551     /* Initialize the list view control */
552     SetListViewColumns(GetDlgItem(hwndDlg, IDC_USERPROFILE_LIST));
553 
554     AddUserProfiles(hwndDlg, GetDlgItem(hwndDlg, IDC_USERPROFILE_LIST));
555 }
556 
557 
558 static
559 VOID
560 OnDestroy(
561     _In_ HWND hwndDlg)
562 {
563     HWND hwndList;
564     INT nItems, i;
565     LVITEM Item;
566 
567     hwndList = GetDlgItem(hwndDlg, IDC_USERPROFILE_LIST);
568 
569     nItems = ListView_GetItemCount(hwndList);
570     for (i = 0; i < nItems; i++)
571     {
572         Item.iItem = i;
573         Item.iSubItem = 0;
574         if (ListView_GetItem(hwndList, &Item))
575         {
576             if (Item.lParam != 0)
577                 HeapFree(GetProcessHeap(), 0, (PVOID)Item.lParam);
578         }
579     }
580 }
581 
582 
583 static
584 VOID
585 OnNotify(
586     _In_ HWND hwndDlg,
587     _In_ NMHDR *nmhdr)
588 {
589     if (nmhdr->idFrom == IDC_USERACCOUNT_LINK && nmhdr->code == NM_CLICK)
590     {
591         ShellExecuteW(hwndDlg, NULL, L"usrmgr.cpl", NULL, NULL, 0);
592     }
593     else if (nmhdr->idFrom == IDC_USERPROFILE_LIST && nmhdr->code == LVN_ITEMCHANGED)
594     {
595         UpdateButtonState(hwndDlg, nmhdr->hwndFrom);
596     }
597 }
598 
599 
600 /* Property page dialog callback */
601 INT_PTR CALLBACK
602 UserProfileDlgProc(HWND hwndDlg,
603                    UINT uMsg,
604                    WPARAM wParam,
605                    LPARAM lParam)
606 {
607     switch (uMsg)
608     {
609         case WM_INITDIALOG:
610             OnInitUserProfileDialog(hwndDlg);
611             return TRUE;
612 
613         case WM_DESTROY:
614             OnDestroy(hwndDlg);
615             break;
616 
617         case WM_COMMAND:
618             switch (LOWORD(wParam))
619             {
620                 case IDOK:
621                 case IDCANCEL:
622                     EndDialog(hwndDlg,
623                               LOWORD(wParam));
624                     return TRUE;
625 
626                 case IDC_USERPROFILE_CHANGE:
627                     ChangeUserProfileType(hwndDlg);
628                     break;
629 
630                 case IDC_USERPROFILE_DELETE:
631                     DeleteUserProfile(hwndDlg);
632                     break;
633 
634                 case IDC_USERPROFILE_COPY:
635                     CopyUserProfile(hwndDlg);
636                     break;
637             }
638             break;
639 
640         case WM_NOTIFY:
641             OnNotify(hwndDlg, (NMHDR *)lParam);
642             break;
643     }
644 
645     return FALSE;
646 }
647