fb49a204874f9321e663d6498a5cbda4cddc2a58
[reactos.git] / dll / cpl / sysdm / userprofile.c
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_ PSID pProfileSid,
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 INT nId, iItem;
340 LV_ITEM lvi;
341
342 dwAccountNameSize = ARRAYSIZE(szAccountName);
343 dwDomainNameSize = ARRAYSIZE(szDomainName);
344 if (!LookupAccountSidW(NULL,
345 pProfileSid,
346 szAccountName,
347 &dwAccountNameSize,
348 szDomainName,
349 &dwDomainNameSize,
350 &Use))
351 {
352 /* Unknown account */
353 LoadStringW(hApplet, IDS_USERPROFILE_ACCOUNT_UNKNOWN, szNameBuffer, ARRAYSIZE(szNameBuffer));
354 }
355 else
356 {
357 /* Show only the user accounts */
358 if (Use != SidTypeUser)
359 return;
360
361 if (szAccountName[0] == UNICODE_NULL)
362 {
363 /* Deleted account */
364 LoadStringW(hApplet, IDS_USERPROFILE_ACCOUNT_DELETED, szNameBuffer, ARRAYSIZE(szNameBuffer));
365 }
366 else
367 {
368 /* Normal account */
369 wsprintf(szNameBuffer, L"%s\\%s", szDomainName, szAccountName);
370 }
371 }
372
373 /* Get the profile state value */
374 dwSize = sizeof(dwState);
375 if (RegQueryValueExW(hProfileKey,
376 L"State",
377 NULL,
378 &dwType,
379 (LPBYTE)&dwState,
380 &dwSize) != ERROR_SUCCESS)
381 {
382 dwState = 0;
383 }
384
385 /* Create and fill the profile data entry */
386 dwProfileData = sizeof(PROFILEDATA) +
387 ((wcslen(szNameBuffer) + 1) * sizeof(WCHAR));
388 pProfileData = HeapAlloc(GetProcessHeap(),
389 0,
390 dwProfileData);
391 if (pProfileData == NULL)
392 return;
393
394 pProfileData->bMyProfile = EqualSid(pMySid, pProfileSid);
395 pProfileData->dwState = dwState;
396
397 ptr = (PWSTR)((ULONG_PTR)pProfileData + sizeof(PROFILEDATA));
398 pProfileData->pszFullName = ptr;
399
400 wcscpy(pProfileData->pszFullName, szNameBuffer);
401
402 /* Add the profile and set its name */
403 memset(&lvi, 0x00, sizeof(lvi));
404 lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
405 lvi.pszText = pProfileData->pszFullName;
406 lvi.state = 0;
407 lvi.lParam = (LPARAM)pProfileData;
408 iItem = ListView_InsertItem(hwndListView, &lvi);
409
410 /* Set the profile type */
411 if (dwState & 0x0001) // PROFILE_MANDATORY
412 nId = IDS_USERPROFILE_MANDATORY;
413 else if (dwState & 0x0010) // PROFILE_UPDATE_CENTRAL
414 nId = IDS_USERPROFILE_ROAMING;
415 else
416 nId = IDS_USERPROFILE_LOCAL;
417
418 LoadStringW(hApplet, nId, szAccountName, ARRAYSIZE(szAccountName));
419
420 ListView_SetItemText(hwndListView, iItem, 2, szAccountName);
421 }
422
423
424 static VOID
425 UpdateButtonState(
426 _In_ HWND hwndDlg,
427 _In_ HWND hwndListView)
428 {
429 LVITEM Item;
430 INT iSelected;
431 BOOL bMyProfile;
432
433 iSelected = ListView_GetNextItem(hwndListView, -1, LVNI_SELECTED);
434 if (iSelected != -1)
435 {
436 Item.mask = LVIF_PARAM;
437 Item.iItem = iSelected;
438 Item.iSubItem = 0;
439 if (ListView_GetItem(hwndListView, &Item))
440 {
441 if (Item.lParam != 0)
442 {
443 bMyProfile = ((PPROFILEDATA)Item.lParam)->bMyProfile;
444 if (!bMyProfile)
445 {
446 EnableWindow(GetDlgItem(hwndDlg, IDC_USERPROFILE_DELETE), TRUE);
447 EnableWindow(GetDlgItem(hwndDlg, IDC_USERPROFILE_COPY), TRUE);
448 }
449 }
450 }
451 EnableWindow(GetDlgItem(hwndDlg, IDC_USERPROFILE_CHANGE), TRUE);
452 }
453 else
454 {
455 EnableWindow(GetDlgItem(hwndDlg, IDC_USERPROFILE_CHANGE), FALSE);
456 EnableWindow(GetDlgItem(hwndDlg, IDC_USERPROFILE_DELETE), FALSE);
457 EnableWindow(GetDlgItem(hwndDlg, IDC_USERPROFILE_COPY), FALSE);
458 }
459 }
460
461
462 static VOID
463 AddUserProfiles(
464 _In_ HWND hwndDlg,
465 _In_ HWND hwndListView,
466 _In_ BOOL bAdmin)
467 {
468 HKEY hKeyUserProfiles = INVALID_HANDLE_VALUE;
469 HKEY hProfileKey;
470 DWORD dwIndex;
471 WCHAR szProfileSid[64];
472 DWORD dwSidLength;
473 FILETIME ftLastWrite;
474 DWORD dwSize;
475 HANDLE hToken = NULL;
476 PTOKEN_USER pTokenUser = NULL;
477 PSID pProfileSid;
478 PWSTR pszProfileSid;
479
480 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
481 return;
482
483 GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize);
484 if (dwSize == 0)
485 goto done;
486
487 pTokenUser = HeapAlloc(GetProcessHeap(), 0, dwSize);
488 if (pTokenUser == NULL)
489 goto done;
490
491 if (!GetTokenInformation(hToken, TokenUser, pTokenUser, dwSize, &dwSize))
492 goto done;
493
494 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
495 L"Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList",
496 0,
497 KEY_READ,
498 &hKeyUserProfiles))
499 goto done;
500
501 if (bAdmin)
502 {
503 for (dwIndex = 0; ; dwIndex++)
504 {
505 dwSidLength = ARRAYSIZE(szProfileSid);
506 if (RegEnumKeyExW(hKeyUserProfiles,
507 dwIndex,
508 szProfileSid,
509 &dwSidLength,
510 NULL,
511 NULL,
512 NULL,
513 &ftLastWrite))
514 break;
515
516 if (RegOpenKeyExW(hKeyUserProfiles,
517 szProfileSid,
518 0,
519 KEY_READ,
520 &hProfileKey) == ERROR_SUCCESS)
521 {
522 if (ConvertStringSidToSid(szProfileSid, &pProfileSid))
523 {
524 AddUserProfile(hwndListView, pProfileSid, pTokenUser->User.Sid, hProfileKey);
525 LocalFree(pProfileSid);
526 }
527
528 RegCloseKey(hProfileKey);
529 }
530 }
531 }
532 else
533 {
534 if (ConvertSidToStringSidW(pTokenUser->User.Sid, &pszProfileSid))
535 {
536 if (RegOpenKeyExW(hKeyUserProfiles,
537 pszProfileSid,
538 0,
539 KEY_READ,
540 &hProfileKey) == ERROR_SUCCESS)
541 {
542 AddUserProfile(hwndListView, pTokenUser->User.Sid, pTokenUser->User.Sid, hProfileKey);
543 RegCloseKey(hProfileKey);
544 }
545
546 LocalFree(pszProfileSid);
547 }
548 }
549
550 if (ListView_GetItemCount(hwndListView) != 0)
551 ListView_SetItemState(hwndListView, 0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
552
553 UpdateButtonState(hwndDlg, hwndListView);
554
555 done:
556 if (hKeyUserProfiles != INVALID_HANDLE_VALUE)
557 RegCloseKey(hKeyUserProfiles);
558
559 if (pTokenUser != NULL)
560 HeapFree(GetProcessHeap(), 0, pTokenUser);
561
562 if (hToken != NULL)
563 CloseHandle(hToken);
564 }
565
566
567 static VOID
568 OnInitUserProfileDialog(HWND hwndDlg)
569 {
570 BOOL bAdmin;
571
572 bAdmin = IsUserAdmin();
573
574 /* Initialize the list view control */
575 SetListViewColumns(GetDlgItem(hwndDlg, IDC_USERPROFILE_LIST));
576
577 /* Hide the delete and copy buttons for non-admins */
578 ShowWindow(GetDlgItem(hwndDlg, IDC_USERPROFILE_DELETE), bAdmin ? SW_SHOW : SW_HIDE);
579 ShowWindow(GetDlgItem(hwndDlg, IDC_USERPROFILE_COPY), bAdmin ? SW_SHOW : SW_HIDE);
580
581 /* Add the profiles to the list view */
582 AddUserProfiles(hwndDlg, GetDlgItem(hwndDlg, IDC_USERPROFILE_LIST), bAdmin);
583 }
584
585
586 static
587 VOID
588 OnDestroy(
589 _In_ HWND hwndDlg)
590 {
591 HWND hwndList;
592 INT nItems, i;
593 LVITEM Item;
594
595 hwndList = GetDlgItem(hwndDlg, IDC_USERPROFILE_LIST);
596
597 nItems = ListView_GetItemCount(hwndList);
598 for (i = 0; i < nItems; i++)
599 {
600 Item.iItem = i;
601 Item.iSubItem = 0;
602 if (ListView_GetItem(hwndList, &Item))
603 {
604 if (Item.lParam != 0)
605 HeapFree(GetProcessHeap(), 0, (PVOID)Item.lParam);
606 }
607 }
608 }
609
610
611 static
612 VOID
613 OnNotify(
614 _In_ HWND hwndDlg,
615 _In_ NMHDR *nmhdr)
616 {
617 if (nmhdr->idFrom == IDC_USERACCOUNT_LINK && nmhdr->code == NM_CLICK)
618 {
619 ShellExecuteW(hwndDlg, NULL, L"usrmgr.cpl", NULL, NULL, 0);
620 }
621 else if (nmhdr->idFrom == IDC_USERPROFILE_LIST && nmhdr->code == LVN_ITEMCHANGED)
622 {
623 UpdateButtonState(hwndDlg, nmhdr->hwndFrom);
624 }
625 }
626
627
628 /* Property page dialog callback */
629 INT_PTR CALLBACK
630 UserProfileDlgProc(HWND hwndDlg,
631 UINT uMsg,
632 WPARAM wParam,
633 LPARAM lParam)
634 {
635 switch (uMsg)
636 {
637 case WM_INITDIALOG:
638 OnInitUserProfileDialog(hwndDlg);
639 return TRUE;
640
641 case WM_DESTROY:
642 OnDestroy(hwndDlg);
643 break;
644
645 case WM_COMMAND:
646 switch (LOWORD(wParam))
647 {
648 case IDOK:
649 case IDCANCEL:
650 EndDialog(hwndDlg,
651 LOWORD(wParam));
652 return TRUE;
653
654 case IDC_USERPROFILE_CHANGE:
655 ChangeUserProfileType(hwndDlg);
656 break;
657
658 case IDC_USERPROFILE_DELETE:
659 DeleteUserProfile(hwndDlg);
660 break;
661
662 case IDC_USERPROFILE_COPY:
663 CopyUserProfile(hwndDlg);
664 break;
665 }
666 break;
667
668 case WM_NOTIFY:
669 OnNotify(hwndDlg, (NMHDR *)lParam);
670 break;
671 }
672
673 return FALSE;
674 }