main
cpp 710 lines 26.2 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 #include "precomp.h"
4
5 LPCWSTR vcsUserQuery = L"SELECT `User`, `Component_`, `Name`, `Domain`, `Comment`, `Password` FROM `Wix4User` WHERE `User`=?";
6 enum eUserQuery { vuqUser = 1, vuqComponent, vuqName, vuqDomain, vuqComment, vuqPassword };
7
8 LPCWSTR vcsGroupQuery = L"SELECT `Group`, `Component_`, `Name`, `Domain` FROM `Wix4Group` WHERE `Group`=?";
9 enum eGroupQuery { vgqGroup = 1, vgqComponent, vgqName, vgqDomain };
10
11 LPCWSTR vcsUserGroupQuery = L"SELECT `User_`, `Group_` FROM `Wix4UserGroup` WHERE `User_`=?";
12 enum eUserGroupQuery { vugqUser = 1, vugqGroup };
13
14 LPCWSTR vActionableQuery = L"SELECT `User`,`Component_`,`Name`,`Domain`,`Password`,`Comment`,`Attributes` FROM `Wix4User` WHERE `Component_` IS NOT NULL";
15 enum eActionableQuery { vaqUser = 1, vaqComponent, vaqName, vaqDomain, vaqPassword, vaqComment, vaqAttributes };
16
17
18 static HRESULT AddUserToList(
19 __inout SCA_USER** ppsuList
20 );
21
22 static HRESULT AddGroupToList(
23 __inout SCA_GROUP** ppsgList
24 );
25
26
27 HRESULT __stdcall ScaGetUser(
28 __in LPCWSTR wzUser,
29 __out SCA_USER* pscau
30 )
31 {
32 if (!wzUser || !pscau)
33 {
34 return E_INVALIDARG;
35 }
36
37 HRESULT hr = S_OK;
38 PMSIHANDLE hView, hRec;
39
40 LPWSTR pwzData = NULL;
41
42 // clear struct and bail right away if no user key was passed to search for
43 ::ZeroMemory(pscau, sizeof(*pscau));
44 if (!*wzUser)
45 {
46 ExitFunction1(hr = S_OK);
47 }
48
49 hRec = ::MsiCreateRecord(1);
50 hr = WcaSetRecordString(hRec, 1, wzUser);
51 ExitOnFailure(hr, "Failed to look up User");
52
53 hr = WcaOpenView(vcsUserQuery, &hView);
54 ExitOnFailure(hr, "Failed to open view on Wix4User table");
55 hr = WcaExecuteView(hView, hRec);
56 ExitOnFailure(hr, "Failed to execute view on Wix4User table");
57
58 hr = WcaFetchSingleRecord(hView, &hRec);
59 if (S_OK == hr)
60 {
61 hr = WcaGetRecordString(hRec, vuqUser, &pwzData);
62 ExitOnFailure(hr, "Failed to get Wix4User.User");
63 hr = ::StringCchCopyW(pscau->wzKey, countof(pscau->wzKey), pwzData);
64 ExitOnFailure(hr, "Failed to copy key string to user object");
65
66 hr = WcaGetRecordString(hRec, vuqComponent, &pwzData);
67 ExitOnFailure(hr, "Failed to get Wix4User.Component_");
68 hr = ::StringCchCopyW(pscau->wzComponent, countof(pscau->wzComponent), pwzData);
69 ExitOnFailure(hr, "Failed to copy component string to user object");
70
71 hr = WcaGetRecordFormattedString(hRec, vuqName, &pwzData);
72 ExitOnFailure(hr, "Failed to get Wix4User.Name");
73 hr = ::StringCchCopyW(pscau->wzName, countof(pscau->wzName), pwzData);
74 ExitOnFailure(hr, "Failed to copy name string to user object");
75
76 hr = WcaGetRecordFormattedString(hRec, vuqDomain, &pwzData);
77 ExitOnFailure(hr, "Failed to get Wix4User.Domain");
78 hr = ::StringCchCopyW(pscau->wzDomain, countof(pscau->wzDomain), pwzData);
79 ExitOnFailure(hr, "Failed to copy domain string to user object");
80
81 hr = WcaGetRecordFormattedString(hRec, vuqComment, &pwzData);
82 ExitOnFailure(hr, "Failed to get Wix4User.Comment");
83 hr = ::StringCchCopyW(pscau->wzComment, countof(pscau->wzComment), pwzData);
84 ExitOnFailure(hr, "Failed to copy comment string to user object");
85
86 hr = WcaGetRecordFormattedString(hRec, vuqPassword, &pwzData);
87 ExitOnFailure(hr, "Failed to get Wix4User.Password");
88 hr = ::StringCchCopyW(pscau->wzPassword, countof(pscau->wzPassword), pwzData);
89 ExitOnFailure(hr, "Failed to copy password string to user object");
90 }
91 else if (E_NOMOREITEMS == hr)
92 {
93 WcaLog(LOGMSG_STANDARD, "Error: Cannot locate Wix4User.User='%ls'", wzUser);
94 hr = E_FAIL;
95 }
96 else
97 {
98 ExitOnFailure(hr, "Error or found multiple matching Wix4User rows");
99 }
100
101 LExit:
102 ReleaseStr(pwzData);
103
104 return hr;
105 }
106
107 HRESULT __stdcall ScaGetUserDeferred(
108 __in LPCWSTR wzUser,
109 __in WCA_WRAPQUERY_HANDLE hUserQuery,
110 __out SCA_USER* pscau
111 )
112 {
113 if (!wzUser || !pscau)
114 {
115 return E_INVALIDARG;
116 }
117
118 HRESULT hr = S_OK;
119 MSIHANDLE hRec, hRecTest;
120
121 LPWSTR pwzData = NULL;
122
123 // clear struct and bail right away if no user key was passed to search for
124 ::ZeroMemory(pscau, sizeof(*pscau));
125 if (!*wzUser)
126 {
127 ExitFunction1(hr = S_OK);
128 }
129
130 // Reset back to the first record
131 WcaFetchWrappedReset(hUserQuery);
132
133 hr = WcaFetchWrappedRecordWhereString(hUserQuery, vuqUser, wzUser, &hRec);
134 if (S_OK == hr)
135 {
136 hr = WcaFetchWrappedRecordWhereString(hUserQuery, vuqUser, wzUser, &hRecTest);
137 if (S_OK == hr)
138 {
139 AssertSz(FALSE, "Found multiple matching Wix4User rows");
140 }
141
142 hr = WcaGetRecordString(hRec, vuqUser, &pwzData);
143 ExitOnFailure(hr, "Failed to get Wix4User.User");
144 hr = ::StringCchCopyW(pscau->wzKey, countof(pscau->wzKey), pwzData);
145 ExitOnFailure(hr, "Failed to copy key string to user object (in deferred CA)");
146
147 hr = WcaGetRecordString(hRec, vuqComponent, &pwzData);
148 ExitOnFailure(hr, "Failed to get Wix4User.Component_");
149 hr = ::StringCchCopyW(pscau->wzComponent, countof(pscau->wzComponent), pwzData);
150 ExitOnFailure(hr, "Failed to copy component string to user object (in deferred CA)");
151
152 hr = WcaGetRecordString(hRec, vuqName, &pwzData);
153 ExitOnFailure(hr, "Failed to get Wix4User.Name");
154 hr = ::StringCchCopyW(pscau->wzName, countof(pscau->wzName), pwzData);
155 ExitOnFailure(hr, "Failed to copy name string to user object (in deferred CA)");
156
157 hr = WcaGetRecordString(hRec, vuqDomain, &pwzData);
158 ExitOnFailure(hr, "Failed to get Wix4User.Domain");
159 hr = ::StringCchCopyW(pscau->wzDomain, countof(pscau->wzDomain), pwzData);
160 ExitOnFailure(hr, "Failed to copy domain string to user object (in deferred CA)");
161
162 hr = WcaGetRecordString(hRec, vuqComment, &pwzData);
163 ExitOnFailure(hr, "Failed to get Wix4User.Comment");
164 hr = ::StringCchCopyW(pscau->wzComment, countof(pscau->wzComment), pwzData);
165 ExitOnFailure(hr, "Failed to copy comment string to user object (in deferred CA)");
166
167 hr = WcaGetRecordString(hRec, vuqPassword, &pwzData);
168 ExitOnFailure(hr, "Failed to get Wix4User.Password");
169 hr = ::StringCchCopyW(pscau->wzPassword, countof(pscau->wzPassword), pwzData);
170 ExitOnFailure(hr, "Failed to copy password string to user object (in deferred CA)");
171 }
172 else if (E_NOMOREITEMS == hr)
173 {
174 WcaLog(LOGMSG_STANDARD, "Error: Cannot locate Wix4User.User='%ls'", wzUser);
175 hr = E_FAIL;
176 }
177 else
178 {
179 ExitOnFailure(hr, "Error fetching single Wix4User row");
180 }
181
182 LExit:
183 ReleaseStr(pwzData);
184
185 return hr;
186 }
187
188
189 HRESULT __stdcall ScaGetGroup(
190 __in LPCWSTR wzGroup,
191 __out SCA_GROUP* pscag
192 )
193 {
194 if (!wzGroup || !pscag)
195 {
196 return E_INVALIDARG;
197 }
198
199 HRESULT hr = S_OK;
200 PMSIHANDLE hView, hRec;
201
202 LPWSTR pwzData = NULL;
203
204 hRec = ::MsiCreateRecord(1);
205 hr = WcaSetRecordString(hRec, 1, wzGroup);
206 ExitOnFailure(hr, "Failed to look up Group");
207
208 hr = WcaOpenView(vcsGroupQuery, &hView);
209 ExitOnFailure(hr, "Failed to open view on Wix4Group table");
210 hr = WcaExecuteView(hView, hRec);
211 ExitOnFailure(hr, "Failed to execute view on Wix4Group table");
212
213 hr = WcaFetchSingleRecord(hView, &hRec);
214 if (S_OK == hr)
215 {
216 hr = WcaGetRecordString(hRec, vgqGroup, &pwzData);
217 ExitOnFailure(hr, "Failed to get Wix4Group.Group.");
218 hr = ::StringCchCopyW(pscag->wzKey, countof(pscag->wzKey), pwzData);
219 ExitOnFailure(hr, "Failed to copy Wix4Group.Group.");
220
221 hr = WcaGetRecordString(hRec, vgqComponent, &pwzData);
222 ExitOnFailure(hr, "Failed to get Wix4Group.Component_");
223 hr = ::StringCchCopyW(pscag->wzComponent, countof(pscag->wzComponent), pwzData);
224 ExitOnFailure(hr, "Failed to copy Wix4Group.Component_.");
225
226 hr = WcaGetRecordFormattedString(hRec, vgqName, &pwzData);
227 ExitOnFailure(hr, "Failed to get Wix4Group.Name");
228 hr = ::StringCchCopyW(pscag->wzName, countof(pscag->wzName), pwzData);
229 ExitOnFailure(hr, "Failed to copy Wix4Group.Name.");
230
231 hr = WcaGetRecordFormattedString(hRec, vgqDomain, &pwzData);
232 ExitOnFailure(hr, "Failed to get Wix4Group.Domain");
233 hr = ::StringCchCopyW(pscag->wzDomain, countof(pscag->wzDomain), pwzData);
234 ExitOnFailure(hr, "Failed to copy Wix4Group.Domain.");
235 }
236 else if (E_NOMOREITEMS == hr)
237 {
238 WcaLog(LOGMSG_STANDARD, "Error: Cannot locate Wix4Group.Group='%ls'", wzGroup);
239 hr = E_FAIL;
240 }
241 else
242 {
243 ExitOnFailure(hr, "Error or found multiple matching Wix4Group rows");
244 }
245
246 LExit:
247 ReleaseStr(pwzData);
248
249 return hr;
250 }
251
252
253 void ScaUserFreeList(
254 __in SCA_USER* psuList
255 )
256 {
257 SCA_USER* psuDelete = psuList;
258 while (psuList)
259 {
260 psuDelete = psuList;
261 psuList = psuList->psuNext;
262
263 ScaGroupFreeList(psuDelete->psgGroups);
264 MemFree(psuDelete);
265 }
266 }
267
268
269 void ScaGroupFreeList(
270 __in SCA_GROUP* psgList
271 )
272 {
273 SCA_GROUP* psgDelete = psgList;
274 while (psgList)
275 {
276 psgDelete = psgList;
277 psgList = psgList->psgNext;
278
279 MemFree(psgDelete);
280 }
281 }
282
283
284 HRESULT ScaUserRead(
285 __out SCA_USER** ppsuList
286 )
287 {
288 //Assert(FALSE);
289 Assert(ppsuList);
290
291 HRESULT hr = S_OK;
292 UINT er = ERROR_SUCCESS;
293 PMSIHANDLE hView, hRec, hUserRec, hUserGroupView;
294
295 LPWSTR pwzData = NULL;
296
297 BOOL fUserGroupExists = FALSE;
298
299 SCA_USER *psu = NULL;
300
301 INSTALLSTATE isInstalled, isAction;
302
303 if (S_OK != WcaTableExists(L"Wix4User"))
304 {
305 WcaLog(LOGMSG_VERBOSE, "Wix4User Table does not exist, exiting");
306 ExitFunction1(hr = S_FALSE);
307 }
308
309 if (S_OK == WcaTableExists(L"Wix4UserGroup"))
310 {
311 fUserGroupExists = TRUE;
312 }
313
314 //
315 // loop through all the users
316 //
317 hr = WcaOpenExecuteView(vActionableQuery, &hView);
318 ExitOnFailure(hr, "failed to open view on Wix4User table");
319 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
320 {
321 hr = WcaGetRecordString(hRec, vaqComponent, &pwzData);
322 ExitOnFailure(hr, "failed to get Wix4User.Component");
323
324 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &isInstalled, &isAction);
325 hr = HRESULT_FROM_WIN32(er);
326 ExitOnFailure(hr, "failed to get Component state for Wix4User");
327
328 // don't bother if we aren't installing or uninstalling this component
329 if (WcaIsInstalling(isInstalled, isAction) || WcaIsUninstalling(isInstalled, isAction))
330 {
331 //
332 // Add the user to the list and populate it's values
333 //
334 hr = AddUserToList(ppsuList);
335 ExitOnFailure(hr, "failed to add user to list");
336
337 psu = *ppsuList;
338
339 psu->isInstalled = isInstalled;
340 psu->isAction = isAction;
341 hr = ::StringCchCopyW(psu->wzComponent, countof(psu->wzComponent), pwzData);
342 ExitOnFailure(hr, "failed to copy component name: %ls", pwzData);
343
344 hr = WcaGetRecordString(hRec, vaqUser, &pwzData);
345 ExitOnFailure(hr, "failed to get Wix4User.User");
346 hr = ::StringCchCopyW(psu->wzKey, countof(psu->wzKey), pwzData);
347 ExitOnFailure(hr, "failed to copy user key: %ls", pwzData);
348
349 hr = WcaGetRecordFormattedString(hRec, vaqName, &pwzData);
350 ExitOnFailure(hr, "failed to get Wix4User.Name");
351 hr = ::StringCchCopyW(psu->wzName, countof(psu->wzName), pwzData);
352 ExitOnFailure(hr, "failed to copy user name: %ls", pwzData);
353
354 hr = WcaGetRecordFormattedString(hRec, vaqDomain, &pwzData);
355 ExitOnFailure(hr, "failed to get Wix4User.Domain");
356 hr = ::StringCchCopyW(psu->wzDomain, countof(psu->wzDomain), pwzData);
357 ExitOnFailure(hr, "failed to copy user domain: %ls", pwzData);
358 hr = WcaGetRecordFormattedString(hRec, vaqComment, &pwzData);
359 ExitOnFailure(hr, "failed to get Wix4User.Comment");
360 hr = ::StringCchCopyW(psu->wzComment, countof(psu->wzComment), pwzData);
361 ExitOnFailure(hr, "failed to copy user comment: %ls", pwzData);
362
363 hr = WcaGetRecordFormattedString(hRec, vaqPassword, &pwzData);
364 ExitOnFailure(hr, "failed to get Wix4User.Password");
365 hr = ::StringCchCopyW(psu->wzPassword, countof(psu->wzPassword), pwzData);
366 ExitOnFailure(hr, "failed to copy user password");
367
368 hr = WcaGetRecordInteger(hRec, vaqAttributes, &psu->iAttributes);
369 ExitOnFailure(hr, "failed to get Wix4User.Attributes");
370
371 // Check if this user is to be added to any groups
372 if (fUserGroupExists)
373 {
374 hUserRec = ::MsiCreateRecord(1);
375 hr = WcaSetRecordString(hUserRec, 1, psu->wzKey);
376 ExitOnFailure(hr, "Failed to create user record for querying Wix4UserGroup table");
377
378 hr = WcaOpenView(vcsUserGroupQuery, &hUserGroupView);
379 ExitOnFailure(hr, "Failed to open view on Wix4UserGroup table for user %ls", psu->wzKey);
380 hr = WcaExecuteView(hUserGroupView, hUserRec);
381 ExitOnFailure(hr, "Failed to execute view on Wix4UserGroup table for user: %ls", psu->wzKey);
382
383 while (S_OK == (hr = WcaFetchRecord(hUserGroupView, &hRec)))
384 {
385 hr = WcaGetRecordString(hRec, vugqGroup, &pwzData);
386 ExitOnFailure(hr, "failed to get Wix4UserGroup.Group");
387
388 hr = AddGroupToList(&(psu->psgGroups));
389 ExitOnFailure(hr, "failed to add group to list");
390
391 hr = ScaGetGroup(pwzData, psu->psgGroups);
392 ExitOnFailure(hr, "failed to get information for group: %ls", pwzData);
393 }
394
395 if (E_NOMOREITEMS == hr)
396 {
397 hr = S_OK;
398 }
399 ExitOnFailure(hr, "failed to enumerate selected rows from Wix4UserGroup table");
400 }
401 }
402 }
403
404 if (E_NOMOREITEMS == hr)
405 {
406 hr = S_OK;
407 }
408 ExitOnFailure(hr, "failed to enumerate selected rows from Wix4User table");
409
410 LExit:
411 ReleaseStr(pwzData);
412
413 return hr;
414 }
415
416
417 static HRESULT WriteGroupInfo(
418 __in SCA_GROUP* psgList,
419 __in LPWSTR *ppwzActionData
420 )
421 {
422 HRESULT hr = S_OK;
423
424 for (SCA_GROUP* psg = psgList; psg; psg = psg->psgNext)
425 {
426 hr = WcaWriteStringToCaData(psg->wzName, ppwzActionData);
427 ExitOnFailure(hr, "failed to add group name to custom action data: %ls", psg->wzName);
428
429 hr = WcaWriteStringToCaData(psg->wzDomain, ppwzActionData);
430 ExitOnFailure(hr, "failed to add group domain to custom action data: %ls", psg->wzDomain);
431 }
432
433 LExit:
434 return hr;
435 }
436
437
438 // Behaves like WriteGroupInfo, but it filters out groups the user is currently a member of,
439 // because we don't want to rollback those
440 static HRESULT WriteGroupRollbackInfo(
441 __in LPCWSTR pwzName,
442 __in LPCWSTR pwzDomain,
443 __in SCA_GROUP* psgList,
444 __in LPWSTR *ppwzActionData
445 )
446 {
447 HRESULT hr = S_OK;
448 BOOL fIsMember = FALSE;
449
450 for (SCA_GROUP* psg = psgList; psg; psg = psg->psgNext)
451 {
452 hr = UserCheckIsMember(pwzName, pwzDomain, psg->wzName, psg->wzDomain, &fIsMember);
453 if (FAILED(hr))
454 {
455 WcaLog(LOGMSG_VERBOSE, "Failed to check if user: %ls (domain: %ls) is member of a group while collecting rollback information (error code 0x%x) - continuing", pwzName, pwzDomain, hr);
456 hr = S_OK;
457 continue;
458 }
459
460 // If the user is currently a member, we don't want to undo that on rollback, so skip adding
461 // this group record to the list of groups to rollback
462 if (fIsMember)
463 {
464 continue;
465 }
466
467 hr = WcaWriteStringToCaData(psg->wzName, ppwzActionData);
468 ExitOnFailure(hr, "failed to add group name to custom action data: %ls", psg->wzName);
469
470 hr = WcaWriteStringToCaData(psg->wzDomain, ppwzActionData);
471 ExitOnFailure(hr, "failed to add group domain to custom action data: %ls", psg->wzDomain);
472 }
473
474 LExit:
475 return hr;
476 }
477
478
479 /* ****************************************************************
480 ScaUserExecute - Schedules user account creation or removal based on
481 component state.
482
483 ******************************************************************/
484 HRESULT ScaUserExecute(
485 __in SCA_USER *psuList
486 )
487 {
488 HRESULT hr = S_OK;
489 DWORD er = 0;
490 LPWSTR pwzDomainName = NULL;
491
492 LPWSTR pwzBaseScriptKey = NULL;
493 DWORD cScriptKey = 0;
494
495 USER_INFO_0 *pUserInfo = NULL;
496 LPWSTR pwzScriptKey = NULL;
497 LPWSTR pwzActionData = NULL;
498 LPWSTR pwzRollbackData = NULL;
499
500 // Get the base script key for this CustomAction.
501 hr = WcaCaScriptCreateKey(&pwzBaseScriptKey);
502 ExitOnFailure(hr, "Failed to get encoding key.");
503
504 // Loop through all the users to be configured.
505 for (SCA_USER *psu = psuList; psu; psu = psu->psuNext)
506 {
507 USER_EXISTS ueUserExists = USER_EXISTS_INDETERMINATE;
508
509 // Always put the User Name, Domain, and Comment on the front of the CustomAction data.
510 // The attributes will be added when we have finished adjusting them. Sometimes we'll
511 // add more data.
512 Assert(psu->wzName);
513 hr = WcaWriteStringToCaData(psu->wzName, &pwzActionData);
514 ExitOnFailure(hr, "Failed to add user name to custom action data: %ls", psu->wzName);
515 hr = WcaWriteStringToCaData(psu->wzDomain, &pwzActionData);
516 ExitOnFailure(hr, "Failed to add user domain to custom action data: %ls", psu->wzDomain);
517 hr = WcaWriteStringToCaData(psu->wzComment, &pwzActionData);
518 ExitOnFailure(hr, "Failed to add user comment to custom action data: %ls", psu->wzComment);
519
520 // Check to see if the user already exists since we have to be very careful when adding
521 // and removing users.
522 hr = GetDomainFromServerName(&pwzDomainName, psu->wzDomain, 0);
523 ExitOnFailure(hr, "Failed to get domain from server name: %ls", psu->wzDomain);
524
525 er = ::NetUserGetInfo(pwzDomainName, psu->wzName, 0, reinterpret_cast<LPBYTE*>(&pUserInfo));
526 if (NERR_Success == er)
527 {
528 ueUserExists = USER_EXISTS_YES;
529 }
530 else if (NERR_UserNotFound == er)
531 {
532 ueUserExists = USER_EXISTS_NO;
533 }
534 else
535 {
536 ueUserExists = USER_EXISTS_INDETERMINATE;
537 hr = HRESULT_FROM_WIN32(er);
538 WcaLog(LOGMSG_VERBOSE, "Failed to check existence of domain: %ls, user: %ls (error code 0x%x) - continuing", pwzDomainName, psu->wzName, hr);
539 hr = S_OK;
540 er = ERROR_SUCCESS;
541 }
542
543 if (WcaIsInstalling(psu->isInstalled, psu->isAction))
544 {
545 // If the user exists, check to see if we are supposed to fail if the user exists before
546 // the install.
547 if (USER_EXISTS_YES == ueUserExists)
548 {
549 // Re-installs will always fail if we don't remove the check for "fail if exists".
550 if (WcaIsReInstalling(psu->isInstalled, psu->isAction))
551 {
552 psu->iAttributes &= ~SCAU_FAIL_IF_EXISTS;
553
554 // If install would create the user, re-install should be able to update the user.
555 if (!(psu->iAttributes & SCAU_DONT_CREATE_USER))
556 {
557 psu->iAttributes |= SCAU_UPDATE_IF_EXISTS;
558 }
559 }
560
561 if (SCAU_FAIL_IF_EXISTS & psu->iAttributes && !(SCAU_UPDATE_IF_EXISTS & psu->iAttributes))
562 {
563 hr = HRESULT_FROM_WIN32(NERR_UserExists);
564 MessageExitOnFailure(hr, msierrUSRFailedUserCreateExists, "Failed to create user: %ls because user already exists.", psu->wzName);
565 }
566 }
567
568 hr = WcaWriteIntegerToCaData(psu->iAttributes, &pwzActionData);
569 ExitOnFailure(hr, "failed to add user attributes to custom action data for user: %ls", psu->wzKey);
570
571 // Rollback only if the user already exists, we couldn't determine if the user exists, or we are going to create the user
572 if ((USER_EXISTS_YES == ueUserExists) || (USER_EXISTS_INDETERMINATE == ueUserExists) || !(psu->iAttributes & SCAU_DONT_CREATE_USER))
573 {
574 ++cScriptKey;
575 hr = StrAllocFormatted(&pwzScriptKey, L"%ls%u", pwzBaseScriptKey, cScriptKey);
576 ExitOnFailure(hr, "Failed to create encoding key.");
577
578 // Write the script key to CustomActionData for install and rollback so information can be passed to rollback.
579 hr = WcaWriteStringToCaData(pwzScriptKey, &pwzActionData);
580 ExitOnFailure(hr, "Failed to add encoding key to custom action data.");
581
582 hr = WcaWriteStringToCaData(pwzScriptKey, &pwzRollbackData);
583 ExitOnFailure(hr, "Failed to add encoding key to rollback custom action data.");
584
585 INT iRollbackUserAttributes = psu->iAttributes;
586
587 // If the user already exists, ensure this is accounted for in rollback
588 if (USER_EXISTS_YES == ueUserExists)
589 {
590 iRollbackUserAttributes |= SCAU_DONT_CREATE_USER;
591 }
592 else
593 {
594 iRollbackUserAttributes &= ~SCAU_DONT_CREATE_USER;
595 }
596
597 // The deferred CA determines when to rollback User Rights Assignments so these should never be set.
598 iRollbackUserAttributes &= ~SCAU_ALLOW_LOGON_AS_SERVICE;
599 iRollbackUserAttributes &= ~SCAU_ALLOW_LOGON_AS_BATCH;
600
601 hr = WcaWriteStringToCaData(psu->wzName, &pwzRollbackData);
602 ExitOnFailure(hr, "Failed to add user name to rollback custom action data: %ls", psu->wzName);
603 hr = WcaWriteStringToCaData(psu->wzDomain, &pwzRollbackData);
604 ExitOnFailure(hr, "Failed to add user domain to rollback custom action data: %ls", psu->wzDomain);
605 hr = WcaWriteIntegerToCaData(iRollbackUserAttributes, &pwzRollbackData);
606 ExitOnFailure(hr, "failed to add user attributes to rollback custom action data for user: %ls", psu->wzKey);
607 // If the user already exists, add relevant group information to rollback data
608 if (USER_EXISTS_YES == ueUserExists || USER_EXISTS_INDETERMINATE == ueUserExists)
609 {
610 hr = WriteGroupRollbackInfo(psu->wzName, psu->wzDomain, psu->psgGroups, &pwzRollbackData);
611 ExitOnFailure(hr, "failed to add group information to rollback custom action data");
612 }
613
614 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"CreateUserRollback"), pwzRollbackData, COST_USER_DELETE);
615 ExitOnFailure(hr, "failed to schedule CreateUserRollback");
616 }
617 else
618 {
619 // Write empty script key to CustomActionData since there is no rollback.
620 hr = WcaWriteStringToCaData(L"", &pwzActionData);
621 ExitOnFailure(hr, "Failed to add empty encoding key to custom action data.");
622 }
623
624 //
625 // Schedule the creation now.
626 //
627 hr = WcaWriteStringToCaData(psu->wzPassword, &pwzActionData);
628 ExitOnFailure(hr, "failed to add user password to custom action data for user: %ls", psu->wzKey);
629
630 // Add user's group information to custom action data
631 hr = WriteGroupInfo(psu->psgGroups, &pwzActionData);
632 ExitOnFailure(hr, "failed to add group information to custom action data");
633
634 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"CreateUser"), pwzActionData, COST_USER_ADD);
635 ExitOnFailure(hr, "failed to schedule CreateUser");
636 }
637 else if (((USER_EXISTS_YES == ueUserExists) || (USER_EXISTS_INDETERMINATE == ueUserExists)) && WcaIsUninstalling(psu->isInstalled, psu->isAction) && !(psu->iAttributes & SCAU_DONT_REMOVE_ON_UNINSTALL))
638 {
639 hr = WcaWriteIntegerToCaData(psu->iAttributes, &pwzActionData);
640 ExitOnFailure(hr, "failed to add user attributes to custom action data for user: %ls", psu->wzKey);
641
642 // Add user's group information - this will ensure the user can be removed from any groups they were added to, if the user isn't be deleted
643 hr = WriteGroupInfo(psu->psgGroups, &pwzActionData);
644 ExitOnFailure(hr, "failed to add group information to custom action data");
645
646 // Schedule the removal because the user exists and we don't have any flags set
647 // that say, don't remove the user on uninstall.
648 //
649 // Note: We can't rollback the removal of a user which is why RemoveUser is a commit
650 // CustomAction.
651 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RemoveUser"), pwzActionData, COST_USER_DELETE);
652 ExitOnFailure(hr, "failed to schedule RemoveUser");
653 }
654
655 ReleaseNullStr(pwzScriptKey);
656 ReleaseNullStr(pwzActionData);
657 ReleaseNullStr(pwzRollbackData);
658 if (pUserInfo)
659 {
660 ::NetApiBufferFree(static_cast<LPVOID>(pUserInfo));
661 pUserInfo = NULL;
662 }
663 }
664
665 LExit:
666 ReleaseStr(pwzBaseScriptKey);
667 ReleaseStr(pwzScriptKey);
668 ReleaseStr(pwzActionData);
669 ReleaseStr(pwzRollbackData);
670 ReleaseStr(pwzDomainName);
671
672 if (pUserInfo)
673 {
674 ::NetApiBufferFree(static_cast<LPVOID>(pUserInfo));
675 }
676
677 return hr;
678 }
679
680
681 static HRESULT AddUserToList(
682 __inout SCA_USER** ppsuList
683 )
684 {
685 HRESULT hr = S_OK;
686 SCA_USER* psu = static_cast<SCA_USER*>(MemAlloc(sizeof(SCA_USER), TRUE));
687 ExitOnNull(psu, hr, E_OUTOFMEMORY, "failed to allocate memory for new user list element");
688
689 psu->psuNext = *ppsuList;
690 *ppsuList = psu;
691
692 LExit:
693 return hr;
694 }
695
696
697 static HRESULT AddGroupToList(
698 __inout SCA_GROUP** ppsgList
699 )
700 {
701 HRESULT hr = S_OK;
702 SCA_GROUP* psg = static_cast<SCA_GROUP*>(MemAlloc(sizeof(SCA_GROUP), TRUE));
703 ExitOnNull(psg, hr, E_OUTOFMEMORY, "failed to allocate memory for new group list element");
704
705 psg->psgNext = *ppsgList;
706 *ppsgList = psg;
707
708 LExit:
709 return hr;
710 }