Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2390 lines
83 KiB

  1. /*++
  2. Microsoft Windows
  3. Copyright (C) Microsoft Corporation, 1981 - 1998
  4. Module Name:
  5. redir.cxx
  6. Abstract:
  7. This module contains the implementation for the members of the class
  8. CRedirInfo which is used to consolidate redirection information from all
  9. the policies applied to a particular GPO and then finally do the redirection
  10. for the policy with the highest precedence.
  11. Author:
  12. Rahul Thombre (RahulTh) 8/11/1998
  13. Revision History:
  14. 8/11/1998 RahulTh Created this module.
  15. --*/
  16. #include "fdeploy.hxx"
  17. //initialize some global variables
  18. WCHAR * g_szRelativePathNames[] =
  19. {
  20. L"Desktop",
  21. L"My Documents",
  22. L"My Documents\\My Pictures",
  23. L"Start Menu",
  24. L"Start Menu\\Programs",
  25. L"Start Menu\\Programs\\Startup",
  26. L"Application Data"
  27. };
  28. WCHAR * g_szDisplayNames[] =
  29. {
  30. L"Desktop",
  31. L"My Documents",
  32. L"My Pictures",
  33. L"Start Menu",
  34. L"Programs",
  35. L"Startup",
  36. L"Application Data"
  37. };
  38. //above: use the same order and elements as in REDIRECTABLE
  39. //global variables.
  40. const int g_lRedirInfoSize = (int) EndRedirectable;
  41. //static members of the class
  42. int CRedirectInfo::m_idConstructor = 0;
  43. CRedirectInfo gPolicyResultant [g_lRedirInfoSize];
  44. CRedirectInfo gDeletedPolicyResultant [g_lRedirInfoSize];
  45. CRedirectInfo gAddedPolicyResultant [g_lRedirInfoSize];
  46. //+--------------------------------------------------------------------------
  47. //
  48. // Member: CRedirectInfo::CRedirectInfo
  49. //
  50. // Synopsis: Constructor for the class
  51. //
  52. // Arguments:
  53. //
  54. // Returns:
  55. //
  56. // History: 8/11/1998 RahulTh created
  57. //
  58. // Notes:
  59. //
  60. //---------------------------------------------------------------------------
  61. CRedirectInfo::CRedirectInfo ()
  62. {
  63. m_rID = (REDIRECTABLE)(m_idConstructor); //assign IDs sequentially.
  64. m_idConstructor = (m_idConstructor + 1) % ((int)EndRedirectable);
  65. m_pSid = NULL;
  66. m_szLocation = NULL;
  67. m_cbLocSize = 0;
  68. m_szGroupRedirectionData = NULL;
  69. ResetMembers ();
  70. }
  71. //+--------------------------------------------------------------------------
  72. //
  73. // Member: CRedirectInfo::~CRedirectInfo
  74. //
  75. // Synopsis: destructor
  76. //
  77. // Arguments:
  78. //
  79. // Returns:
  80. //
  81. // History: 10/6/1998 RahulTh created
  82. //
  83. // Notes:
  84. //
  85. //---------------------------------------------------------------------------
  86. CRedirectInfo::~CRedirectInfo ()
  87. {
  88. FreeAllocatedMem ();
  89. }
  90. //+--------------------------------------------------------------------------
  91. //
  92. // Member: CRedirectInfo::FreeAllocatedMem
  93. //
  94. // Synopsis: frees memory allocated for member vars.
  95. //
  96. // Arguments: none.
  97. //
  98. // Returns: nothing.
  99. //
  100. // History: 12/17/2000 RahulTh created
  101. //
  102. // Notes:
  103. //
  104. //---------------------------------------------------------------------------
  105. void CRedirectInfo::FreeAllocatedMem (void)
  106. {
  107. if (m_szLocation)
  108. {
  109. delete [] m_szLocation;
  110. m_szLocation = NULL;
  111. m_cbLocSize = 0;
  112. }
  113. if (m_pSid)
  114. {
  115. delete [] ((BYTE *)m_pSid);
  116. m_pSid = NULL;
  117. }
  118. if (m_szGroupRedirectionData)
  119. {
  120. delete [] m_szGroupRedirectionData;
  121. m_szGroupRedirectionData = NULL;
  122. }
  123. }
  124. //+--------------------------------------------------------------------------
  125. //
  126. // Member: CRedirectInfo::ResetMembers
  127. //
  128. // Synopsis: resets the members of the class to their default values.
  129. //
  130. // Arguments: none.
  131. //
  132. // Returns: nothing.
  133. //
  134. // History: 12/17/2000 RahulTh created
  135. //
  136. // Notes: static members of a class, like other global variables are
  137. // initialized only when the dll is loaded. So if this dll
  138. // stays loaded across logons, then the fact that the globals
  139. // have been initialized based on a previous logon can cause
  140. // problems. Therefore, this function is used to reinitialize
  141. // the globals.
  142. //
  143. //---------------------------------------------------------------------------
  144. void CRedirectInfo::ResetMembers(void)
  145. {
  146. FreeAllocatedMem ();
  147. m_iRedirectingGroup = 0;
  148. //defaults. are changed later on based on the state of this object.
  149. m_bFollowsParent = FALSE;
  150. m_bRedirectionAttempted = FALSE;
  151. m_StatusRedir = ERROR_SUCCESS;
  152. m_bValidGPO = FALSE;
  153. m_szGPOName[0] = L'\0';
  154. lstrcpy (m_szFolderRelativePath, g_szRelativePathNames [(int) m_rID]);
  155. lstrcpy (m_szDisplayName, g_szDisplayNames [(int) m_rID]);
  156. m_fDataValid = FALSE;
  157. m_cbLocSize = 256; //start with a random amount
  158. m_szLocation = new WCHAR [m_cbLocSize];
  159. if (m_szLocation)
  160. {
  161. m_szLocation[0] = '\0';
  162. }
  163. else
  164. {
  165. m_cbLocSize = 0; //don't worry right now if memory cannot be allocated here, we will fail later
  166. }
  167. //set the parent and child pointers for the special parent/descendants
  168. m_pChild = NULL; //start with defaults
  169. m_pParent = NULL;
  170. switch (m_rID)
  171. {
  172. case MyDocs:
  173. m_pChild = this - (int) m_rID + (int) MyPics;
  174. m_dwFlags = REDIR_DONT_CARE;
  175. break;
  176. case MyPics:
  177. m_pParent = this - (int) m_rID + (int) MyDocs;
  178. m_dwFlags = REDIR_DONT_CARE;
  179. break;
  180. case StartMenu:
  181. m_pChild = this - (int) m_rID + (int) Programs;
  182. m_dwFlags = REDIR_DONT_CARE;
  183. break;
  184. case Programs:
  185. m_pParent = this - (int) m_rID + (int) StartMenu;
  186. m_pChild = this - (int) m_rID + (int) Startup;
  187. m_dwFlags = REDIR_DONT_CARE;
  188. break;
  189. case Startup:
  190. m_pParent = this - (int) m_rID + (int) Programs;
  191. m_dwFlags = REDIR_DONT_CARE;
  192. break;
  193. case Desktop:
  194. m_pChild = NULL;
  195. m_pParent = NULL;
  196. m_dwFlags = REDIR_DONT_CARE;
  197. break;
  198. case AppData:
  199. m_pChild = NULL;
  200. m_pParent = NULL;
  201. m_dwFlags = REDIR_DONT_CARE;
  202. break;
  203. }
  204. //as a safety mechanism, load localized folder names if ghDllInstance
  205. //has been set. note: ghDllInstance won't be set for global variables
  206. //since their constructors before DllMain. For such variables, the
  207. //localized names have to be called explicitly from some other function
  208. //which is called after DllMain.
  209. if (ghDllInstance)
  210. LoadLocalizedNames();
  211. //
  212. // No need to modify m_rID or m_idConstructor. The construct has already
  213. // taken care of it and touching them might cause undesirable results if
  214. // not done in the proper order. Besides, these don't change across multiple
  215. // sessions.
  216. //
  217. }
  218. //+--------------------------------------------------------------------------
  219. //
  220. // Member: CRedirectInfo::GetFolderIndex
  221. //
  222. // Synopsis: a static member function that, given the name of a special
  223. // folder, returns its id which can be used to locate the
  224. // redirection info. for that particular folder.
  225. //
  226. // Arguments: [in][szFldrName : the name of the folder as stored in fdeploy.ini
  227. //
  228. // Returns: the id of the folder. If the folder is not redirectable, the
  229. // function returns EndRedirectable.
  230. //
  231. // History: 8/11/1998 RahulTh created
  232. //
  233. // Notes:
  234. //
  235. //---------------------------------------------------------------------------
  236. REDIRECTABLE CRedirectInfo::GetFolderIndex (LPCTSTR szFldrName)
  237. {
  238. int i;
  239. for (i = 0; i < (int)EndRedirectable; i++)
  240. {
  241. if (0 == lstrcmpi (szFldrName, g_szDisplayNames[i]))
  242. break; //we have found a match
  243. }
  244. return (REDIRECTABLE)i; //if a match was not found above, i == EndRedirectable
  245. }
  246. //+--------------------------------------------------------------------------
  247. //
  248. // Member: CRedirectInfo::LoadLocalizedNames
  249. //
  250. // Synopsis: loads the localized folder display names and folder relative
  251. // paths from the resources.
  252. //
  253. // Arguments: none.
  254. //
  255. // Returns: ERROR_SUCCESS if the names were successfully loaded.
  256. // an error code describing the cause of the failure otherwise.
  257. //
  258. // History: 5/6/1999 RahulTh created
  259. //
  260. // Notes: we cannot do this in the constructor because ghDllInstance
  261. // is not initialized at that time and therefore LoadString will
  262. // fail. This is because DllMain is called after the constructors.
  263. //
  264. //---------------------------------------------------------------------------
  265. DWORD CRedirectInfo::LoadLocalizedNames (void)
  266. {
  267. UINT DisplayID;
  268. UINT RelpathID;
  269. switch (m_rID)
  270. {
  271. case MyDocs:
  272. DisplayID = RelpathID = IDS_MYDOCS;
  273. break;
  274. case MyPics:
  275. DisplayID = IDS_MYPICS;
  276. RelpathID = IDS_MYPICS_REL;
  277. break;
  278. case StartMenu:
  279. DisplayID = RelpathID = IDS_STARTMENU;
  280. break;
  281. case Programs:
  282. DisplayID = IDS_PROGRAMS;
  283. RelpathID = IDS_PROGRAMS_REL;
  284. break;
  285. case Startup:
  286. DisplayID = IDS_STARTUP;
  287. RelpathID = IDS_STARTUP_REL;
  288. break;
  289. case Desktop:
  290. DisplayID = RelpathID = IDS_DESKTOP;
  291. break;
  292. case AppData:
  293. DisplayID = RelpathID = IDS_APPDATA;
  294. break;
  295. }
  296. //now get the localized name of the folder and the localized relative
  297. //path names (w.r.t. the userprofile directory)
  298. m_szLocDisplayName[0] = m_szLocFolderRelativePath[0] = '\0'; //safety
  299. if (!LoadString (ghDllInstance, DisplayID, m_szLocDisplayName, 80))
  300. return GetLastError();
  301. if (DisplayID == RelpathID)
  302. {
  303. lstrcpy (m_szLocFolderRelativePath, m_szLocDisplayName); //top level folders
  304. }
  305. else
  306. {
  307. if (!LoadString (ghDllInstance, RelpathID, m_szLocFolderRelativePath, 80)) //special descendant folders
  308. return GetLastError();
  309. }
  310. return ERROR_SUCCESS;
  311. }
  312. //+--------------------------------------------------------------------------
  313. //
  314. // Member: CRedirectInfo::GatherRedirectionInfo
  315. //
  316. // Synopsis: this function gathers redirection info. from the ini file
  317. //
  318. // Arguments: [in] pFileDB : pointer to the CFileDB object that called it
  319. // [in] dwFlags : the flags as obtained from the ini file
  320. // [in] bRemove : whether this is a policy that is being removed
  321. //
  322. // Returns: STATUS_SUCCESS if successful. An error code otherwise.
  323. //
  324. // History: 8/11/1998 RahulTh created
  325. //
  326. // Notes:
  327. //
  328. //---------------------------------------------------------------------------
  329. DWORD CRedirectInfo::GatherRedirectionInfo (CFileDB * pFileDB, DWORD dwFlags, BOOL bRemove)
  330. {
  331. DWORD Status = ERROR_SUCCESS;
  332. DWORD len;
  333. BOOL bStatus;
  334. WCHAR * pwszSection = 0;
  335. BOOL bFoundGroup;
  336. WCHAR * pwszString = 0;
  337. WCHAR * pwszSid = 0;
  338. WCHAR * pwszPath = 0;
  339. PSID Sid = NULL;
  340. if (bRemove &&
  341. (!gSavedSettings[m_rID].m_bValidGPO ||
  342. (0 != _wcsicmp (pFileDB->_pwszGPOUniqueName, gSavedSettings[m_rID].m_szGPOName))
  343. )
  344. )
  345. {
  346. DebugMsg((DM_VERBOSE, IDS_IGNORE_DELETEDGPO, pFileDB->_pwszGPOName, pFileDB->_pwszGPOUniqueName, m_szLocDisplayName));
  347. Status = ERROR_SUCCESS;
  348. //set default values on the members just to be on the safe side.
  349. m_fDataValid = FALSE;
  350. m_dwFlags = REDIR_DONT_CARE;
  351. m_bValidGPO = FALSE;
  352. m_szGPOName[0] = L'\0';
  353. goto GatherInfoEnd;
  354. }
  355. //the data is valid. This function is only called when something relevant is found in the ini file.
  356. m_fDataValid = TRUE;
  357. //store the flags
  358. m_dwFlags = dwFlags;
  359. //store the GPO's unique name
  360. if (bRemove)
  361. {
  362. m_bValidGPO = FALSE; //for redirection resulting from a removed GPO, we do not store the GPO name to avoid processing a removal twice
  363. m_szGPOName[0] = L'\0';
  364. }
  365. else
  366. {
  367. m_bValidGPO = TRUE;
  368. wcscpy (m_szGPOName, pFileDB->_pwszGPOUniqueName);
  369. }
  370. //there is nothing to do if policy is not specified for this folder
  371. if (m_dwFlags & REDIR_DONT_CARE)
  372. goto GatherInfoSuccess;
  373. m_bRemove = bRemove;
  374. //also, we can do nothing right now if this is a special descendant folder
  375. //following its parent
  376. if (m_dwFlags & REDIR_FOLLOW_PARENT)
  377. goto GatherInfoSuccess;
  378. //a location has been specified by this policy
  379. if ( ! pFileDB->GetRsopContext()->IsPlanningModeEnabled() )
  380. {
  381. //we must have a list of groups to which the user belongs
  382. //each user must at least have everyone as one of his groups
  383. if (! pFileDB->_pGroups)
  384. goto GatherInfoErr;
  385. }
  386. DWORD cchSectionLen;
  387. bStatus = pFileDB->ReadIniSection (m_szDisplayName, &pwszSection, &cchSectionLen);
  388. if (!bStatus)
  389. goto GatherInfoErr;
  390. bFoundGroup = FALSE;
  391. //
  392. // For rsop, we need to know the list of security groups
  393. // and redirected paths -- we'll copy this information for
  394. // use by the rsop logging code
  395. //
  396. if (pFileDB->GetRsopContext()->IsRsopEnabled())
  397. {
  398. //
  399. // Note that when we do the copy, the size returned
  400. // by ReadIniSection above does not include the null terminator,
  401. // even though thaft character is present, so we must add that character
  402. // to the count ourselves.
  403. //
  404. m_szGroupRedirectionData = new WCHAR[cchSectionLen + 1];
  405. if (m_szGroupRedirectionData)
  406. {
  407. RtlCopyMemory(m_szGroupRedirectionData,
  408. pwszSection,
  409. ( cchSectionLen + 1 ) * sizeof(*pwszSection));
  410. }
  411. else
  412. {
  413. pFileDB->GetRsopContext()->DisableRsop( E_OUTOFMEMORY );
  414. }
  415. }
  416. DWORD iGroup;
  417. iGroup = 0;
  418. for (pwszString = pwszSection;
  419. *pwszString;
  420. pwszString += lstrlen(pwszString) + 1)
  421. {
  422. pwszSid = pwszString;
  423. pwszPath = wcschr (pwszString, L'=');
  424. if (!pwszPath)
  425. continue; //skip any invalid entries
  426. //temporarily break up the sid and the path
  427. *pwszPath++ = L'\0';
  428. if (! *pwszPath)
  429. {
  430. //again an invalid path. restore the = sign and move on
  431. pwszPath[-1] = L'='; //note: we had advanced the pointer above
  432. continue;
  433. }
  434. //the entry is valid
  435. bFoundGroup = GroupInList (pwszSid, pFileDB->_pGroups);
  436. if (bFoundGroup)
  437. {
  438. m_iRedirectingGroup = iGroup;
  439. break; //we have found a group, so break out of the loop
  440. }
  441. else
  442. {
  443. //restore the '=' sign, and try the next group
  444. pwszPath[-1] = L'='; //note: we had advanced the pointer above
  445. }
  446. iGroup++;
  447. }
  448. if (!bFoundGroup)
  449. {
  450. //no group was found, so treat this as a don't care
  451. m_dwFlags = REDIR_DONT_CARE;
  452. }
  453. else
  454. {
  455. //first store the sid
  456. if (m_pSid)
  457. delete [] ((BYTE*) m_pSid); //m_pSid is always allocated from our heap, so never use RtlFreeSid
  458. m_pSid = NULL;
  459. if (!m_bRemove)
  460. Status = AllocateAndInitSidFromString (pwszSid, &Sid);
  461. else
  462. Status = AllocateAndInitSidFromString (L"S-1-1-0", &Sid); //if this is a removed policy, we set the SID to Everyone -- so that when we look at the saved settings at a later time, we shouldn't have to process the policy again (see code for NeedsProcessing)
  463. if (ERROR_SUCCESS != Status)
  464. goto GatherInfoEnd;
  465. if (m_pSid)
  466. delete [] ((BYTE*) m_pSid);
  467. Status = MySidCopy (&m_pSid, Sid); //we want to always allocate memory for this sid from our heap
  468. RtlFreeSid (Sid); //cleanup. must take place before the following check
  469. if (ERROR_SUCCESS != Status)
  470. goto GatherInfoEnd;
  471. //we have found a group
  472. DebugMsg ((DM_VERBOSE, IDS_GROUP_MEMBER, pwszSid, pwszPath));
  473. SimplifyPath (pwszPath);
  474. //
  475. // Copy the location
  476. // Use X:\ for paths of the form X: to avoid problems during the file
  477. // copy phase.
  478. //
  479. BOOL bAppendSlash = FALSE;
  480. len = lstrlen (pwszPath);
  481. if (2 == len && L':' == pwszPath[1])
  482. {
  483. bAppendSlash = TRUE;
  484. len++; // Ensure that the extra backslash at the end is accounted for in length calculations.
  485. }
  486. if (m_cbLocSize <= len)
  487. {
  488. //we need to reallocate memory for the location
  489. if (m_cbLocSize)
  490. delete [] m_szLocation;
  491. m_cbLocSize = len + 1; // Add one for the terminating null.
  492. m_szLocation = new TCHAR [m_cbLocSize];
  493. if (!m_szLocation)
  494. {
  495. m_cbLocSize = 0;
  496. goto GatherInfoErr;
  497. }
  498. }
  499. lstrcpy (m_szLocation, pwszPath);
  500. if (bAppendSlash)
  501. lstrcat (m_szLocation, L"\\");
  502. }
  503. GatherInfoSuccess:
  504. DebugMsg ((DM_VERBOSE, IDS_COLLECT_REDIRINFO, m_szLocDisplayName, m_dwFlags));
  505. Status = STATUS_SUCCESS;
  506. goto GatherInfoEnd;
  507. GatherInfoErr:
  508. Status = ERROR_OUTOFMEMORY;
  509. m_fDataValid = FALSE;
  510. m_dwFlags = REDIR_DONT_CARE; //so that these settings will be ignored while merging into global data
  511. DebugMsg ((DM_VERBOSE, IDS_GATHER_FAILURE, Status, m_szLocDisplayName));
  512. GatherInfoEnd:
  513. if (pwszSection)
  514. delete [] pwszSection;
  515. return Status;
  516. }
  517. //+--------------------------------------------------------------------------
  518. //
  519. // Member: CRedirectInfo::WasRedirectionAttempted
  520. //
  521. // Synopsis: indicates whether redirection has already been attempted on
  522. // this folder.
  523. //
  524. // Arguments: none.
  525. //
  526. // Returns: a bool indicating the required status
  527. //
  528. // History: 9/20/1999 RahulTh created
  529. //
  530. // Notes:
  531. //
  532. //---------------------------------------------------------------------------
  533. const BOOL CRedirectInfo::WasRedirectionAttempted (void)
  534. {
  535. return m_bRedirectionAttempted;
  536. }
  537. //+--------------------------------------------------------------------------
  538. //
  539. // Member: CRedirectInfo::GetFlags
  540. //
  541. // Synopsis: returns the redirection flags for the folder represented by
  542. // this object
  543. //
  544. // Arguments: none
  545. //
  546. // Returns: the value of the flags
  547. //
  548. // History: 8/12/1998 RahulTh created
  549. //
  550. // Notes:
  551. //
  552. //---------------------------------------------------------------------------
  553. const DWORD CRedirectInfo::GetFlags (void)
  554. {
  555. return m_dwFlags;
  556. }
  557. //+--------------------------------------------------------------------------
  558. //
  559. // Member: CRedirectInfo::GetRedirStatus
  560. //
  561. // Synopsis: retrieves the return code of the redirection operation
  562. //
  563. // Arguments: none
  564. //
  565. // Returns: the member m_StatusRedir
  566. //
  567. // History: 5/3/1999 RahulTh created
  568. //
  569. // Notes:
  570. //
  571. //---------------------------------------------------------------------------
  572. const DWORD CRedirectInfo::GetRedirStatus (void)
  573. {
  574. return m_StatusRedir;
  575. }
  576. //+--------------------------------------------------------------------------
  577. //
  578. // Member: CRedirectInfo::GetLocation
  579. //
  580. // Synopsis: returns the redirection location for the folder represented
  581. // by this object
  582. //
  583. // Arguments: none
  584. //
  585. // Returns: a pointer to the buffer containing the path
  586. //
  587. // History: 8/12/1998 RahulTh created
  588. //
  589. // Notes:
  590. //
  591. //---------------------------------------------------------------------------
  592. LPCTSTR CRedirectInfo::GetLocation (void)
  593. {
  594. return m_szLocation;
  595. }
  596. //+--------------------------------------------------------------------------
  597. //
  598. // Member: CRedirectInfo::GetLocalizedName
  599. //
  600. // Synopsis: returns the localized name of the folder
  601. //
  602. // Arguments: none
  603. //
  604. // Returns: a pointer to the buffer containing the localized name
  605. //
  606. // History: 8/12/1998 RahulTh created
  607. //
  608. // Notes:
  609. //
  610. //---------------------------------------------------------------------------
  611. LPCTSTR CRedirectInfo::GetLocalizedName (void)
  612. {
  613. return m_szLocDisplayName;
  614. }
  615. //+--------------------------------------------------------------------------
  616. //
  617. // Member: CRedirectInfo::Redirect
  618. //
  619. // Synopsis: this function performs the actual redirection for the
  620. // folder represented by this object.
  621. //
  622. // Arguments: [in] hUserToken : handle to the user token
  623. // [in] hKeyRoot : handle to HKCU
  624. // [in] pFileDB : pointer to the CFileDB object from which this
  625. // function was called.
  626. //
  627. // Returns: a DWORD indicating the success/failure code in redirection
  628. //
  629. // History: 8/12/1998 RahulTh created
  630. //
  631. // Notes:
  632. // It is very important to record the return code in m_StatusRedir
  633. // before returning from this function. If we don't do that
  634. // we might end up returning the wrong value to the policy engine
  635. // while attempting to redirect special descendant folders which
  636. // can affect the list of policies obtained at a subsequent logon
  637. // session.
  638. //
  639. //---------------------------------------------------------------------------
  640. DWORD CRedirectInfo::Redirect (HANDLE hUserToken,
  641. HKEY hKeyRoot,
  642. CFileDB* pFileDB)
  643. {
  644. SHARESTATUS SourceStatus = NoCSC;
  645. SHARESTATUS DestStatus = NoCSC;
  646. DWORD PinStatus = ERROR_SUCCESS;
  647. WCHAR * wszProcessedPath = NULL;
  648. if (m_bRedirectionAttempted)
  649. {
  650. goto Redir_CleanupAndQuit; //redirection has already been attempted
  651. //and the success / failure code has been
  652. //recorded in m_StatusRedir. Use the same
  653. //error code as the return value as this
  654. //value may not yet have been recorded in
  655. //ProcessGroupPolicy
  656. }
  657. //we shall now attempt to redirect...
  658. m_bRedirectionAttempted = TRUE;
  659. if (m_dwFlags & REDIR_FOLLOW_PARENT)
  660. {
  661. //this is possible only if UpdateDescendant ran out of memory
  662. //which means that we could not derive the folder's redirection
  663. //info. No point trying to log an event since we are out of memory
  664. //anyway. we just try to quit as gracefully as we can.
  665. m_StatusRedir = ERROR_OUTOFMEMORY;
  666. goto Redir_KillChildAndLeave;
  667. }
  668. WCHAR wszExpandedNewPath [TARGETPATHLIMIT];
  669. WCHAR wszExpandedCurrentPath [TARGETPATHLIMIT];
  670. WCHAR wszHackedName [TARGETPATHLIMIT];
  671. WCHAR wszExpandedSavedFolderPath [TARGETPATHLIMIT];
  672. WCHAR wszExpandedNewFolderPath [TARGETPATHLIMIT];
  673. CSavedSettings * pLocalSettings;
  674. BOOL bCurrentPathValid; //indicates if we have been successful in obtaining the current path of the folder.
  675. UNICODE_STRING Path;
  676. UNICODE_STRING ExpandedPath;
  677. //set some defaults.
  678. m_StatusRedir = ERROR_SUCCESS;
  679. bCurrentPathValid = TRUE;
  680. //if the policy cares about the location of the folder, then we must
  681. //expand the path
  682. if (! (m_dwFlags & REDIR_DONT_CARE))
  683. {
  684. //show additional status messages if the verbose status is on.
  685. DisplayStatusMessage (IDS_REDIR_CALLBACK);
  686. if (!m_cbLocSize)
  687. {
  688. m_StatusRedir = ERROR_OUTOFMEMORY;
  689. //we had run out of memory so no point trying to log an event
  690. //we just quit as gracefully as we can.
  691. goto Redir_KillChildAndLeave;
  692. }
  693. if (m_bRemove)
  694. {
  695. wcscpy ( m_szLocation, L"%USERPROFILE%\\");
  696. wcscat ( m_szLocation, m_szLocFolderRelativePath);
  697. }
  698. // Get the expanded destination path.
  699. // First expand the homedir component, if applicable.
  700. m_StatusRedir = ExpandHomeDirPolicyPath (m_rID,
  701. m_szLocation,
  702. m_bFollowsParent,
  703. &wszProcessedPath);
  704. if (ERROR_SUCCESS == m_StatusRedir)
  705. {
  706. Path.Length = (wcslen (wszProcessedPath) + 1) * sizeof (WCHAR);
  707. Path.MaximumLength = sizeof (wszProcessedPath);
  708. Path.Buffer = wszProcessedPath;
  709. ExpandedPath.Length = 0;
  710. ExpandedPath.MaximumLength = sizeof (wszExpandedNewFolderPath);
  711. ExpandedPath.Buffer = wszExpandedNewFolderPath;
  712. m_StatusRedir = RtlExpandEnvironmentStrings_U (
  713. pFileDB->_pEnvBlock,
  714. &Path,
  715. &ExpandedPath,
  716. NULL
  717. );
  718. if (STATUS_BUFFER_TOO_SMALL == m_StatusRedir)
  719. {
  720. gpEvents->Report (
  721. EVENT_FDEPLOY_DESTPATH_TOO_LONG,
  722. 3,
  723. m_szLocDisplayName,
  724. m_szLocation,
  725. NumberToString ( TARGETPATHLIMIT )
  726. );
  727. goto Redir_KillChildAndLeave;
  728. }
  729. }
  730. if (ERROR_SUCCESS != m_StatusRedir)
  731. {
  732. DebugMsg ((DM_WARNING, IDS_REDIRECT_EXP_FAIL, m_szLocation, m_StatusRedir));
  733. gpEvents->Report (
  734. EVENT_FDEPLOY_FOLDER_EXPAND_FAIL,
  735. 2,
  736. m_szLocDisplayName,
  737. StatusToString ( m_StatusRedir )
  738. );
  739. goto Redir_KillChildAndLeave;
  740. }
  741. }
  742. pLocalSettings = & (gSavedSettings[(int) m_rID]);
  743. if (pLocalSettings->m_dwFlags & REDIR_DONT_CARE)
  744. {
  745. if (m_dwFlags & REDIR_DONT_CARE)
  746. {
  747. pLocalSettings->Save (pLocalSettings->m_szCurrentPath, m_dwFlags, NULL, NULL);
  748. if (MyDocs == m_rID)
  749. RestrictMyDocsRedirection (hUserToken, hKeyRoot, FALSE);
  750. m_StatusRedir = ERROR_SUCCESS;
  751. goto Redir_CleanupAndQuit;
  752. }
  753. DebugMsg ((DM_VERBOSE, IDS_REDIRECT, m_szLocDisplayName, m_szLocation));
  754. //the policy cares about the location of this doc.
  755. //must get the correct syntax for GetLocalFilePath
  756. lstrcpy (wszHackedName, m_szFolderRelativePath);
  757. lstrcat (wszHackedName, L"\\");
  758. m_StatusRedir = pFileDB->GetLocalFilePath (wszHackedName, wszExpandedCurrentPath);
  759. //
  760. // Expand the homedir if necessary. Note: GetLocalFilePath will not
  761. // do it because it calls SHGetFolderPath and HOMESHARE and HOMEPATH
  762. // are not defined at that point.
  763. //
  764. if (ERROR_SUCCESS == m_StatusRedir)
  765. {
  766. m_StatusRedir = ExpandHomeDir (m_rID,
  767. wszExpandedCurrentPath,
  768. TRUE,
  769. &wszProcessedPath,
  770. pLocalSettings->m_bHomedirChanged ? pLocalSettings->m_szLastHomedir : NULL
  771. );
  772. if (ERROR_SUCCESS != m_StatusRedir ||
  773. ! wszProcessedPath ||
  774. TARGETPATHLIMIT <= lstrlen (wszProcessedPath))
  775. {
  776. m_StatusRedir = (ERROR_SUCCESS == m_StatusRedir) ? ERROR_BUFFER_OVERFLOW : m_StatusRedir;
  777. }
  778. else
  779. {
  780. lstrcpy (wszExpandedCurrentPath, wszProcessedPath);
  781. }
  782. }
  783. //GetLocalFilePath might fail if SHGetFolderPath fails. SHGetFolderPath
  784. //fails if the registry keys for the folder point to an invalid path and
  785. //there is nothing in the CSC cache for it either. So in this particular
  786. //case, we ignore the failure and treat it as a NO_MOVE. In NO_MOVE, we
  787. //do not require the current path of the folder.
  788. if ((ERROR_SUCCESS != m_StatusRedir) &&
  789. ERROR_INVALID_NAME != m_StatusRedir) //the only possible error from GetLocalFilePath not generated by SHGetFolderPath
  790. {
  791. m_dwFlags &= ~REDIR_MOVE_CONTENTS;
  792. m_StatusRedir = ERROR_SUCCESS;
  793. bCurrentPathValid = FALSE;
  794. wcscpy (wszExpandedCurrentPath, L"???"); //if bCurrentPathValid is FALSE, this string will only be used in logs
  795. }
  796. if (m_StatusRedir != ERROR_SUCCESS)
  797. {
  798. DebugMsg ((DM_WARNING, IDS_REDIRECT_NO_LOCAL, m_szLocDisplayName, m_StatusRedir));
  799. gpEvents->Report (
  800. EVENT_FDEPLOY_FOLDER_EXPAND_FAIL,
  801. 2,
  802. m_szLocDisplayName,
  803. StatusToString ( m_StatusRedir )
  804. );
  805. goto Redir_KillChildAndLeave;
  806. }
  807. //make sure that it is not already redirected
  808. if (bCurrentPathValid && (0 == _wcsicmp (wszExpandedCurrentPath, wszExpandedNewFolderPath)))
  809. {
  810. pLocalSettings->Save (m_szLocation, m_dwFlags, m_pSid, m_bValidGPO ? m_szGPOName : NULL);
  811. if (MyDocs == m_rID)
  812. RestrictMyDocsRedirection (hUserToken, hKeyRoot,
  813. (m_bRemove || (m_dwFlags & REDIR_DONT_CARE))?FALSE:TRUE);
  814. DebugMsg ((DM_VERBOSE, IDS_REDIRECT_INSYNC, m_szLocDisplayName,
  815. wszExpandedNewFolderPath));
  816. //however, it is possible that the folders have not been pinned.
  817. //e.g. a roaming user who has already been redirected on one machine
  818. //and now logs on to another machine for the first time.
  819. DestStatus = GetCSCStatus (wszExpandedNewFolderPath);
  820. if (ShareOnline == DestStatus)
  821. {
  822. PinStatus = PinIfNecessary (wszExpandedNewFolderPath, DestStatus);
  823. if ( ERROR_SUCCESS != PinStatus )
  824. DebugMsg((DM_VERBOSE, IDS_CSCPIN_FAIL,
  825. m_szLocDisplayName, PinStatus));
  826. CacheDesktopIni (wszExpandedNewFolderPath, DestStatus, PinFile);
  827. }
  828. m_StatusRedir = ERROR_SUCCESS;
  829. goto Redir_CleanupAndQuit;
  830. }
  831. if (g_bCSCEnabled)
  832. {
  833. SourceStatus = bCurrentPathValid ? GetCSCStatus (wszExpandedCurrentPath) : PathLocal;
  834. DestStatus = GetCSCStatus (wszExpandedNewFolderPath);
  835. if (ShareOffline == DestStatus ||
  836. ((m_dwFlags & REDIR_MOVE_CONTENTS) && ShareOffline == SourceStatus))
  837. {
  838. m_StatusRedir = ERROR_CSCSHARE_OFFLINE;
  839. gpEvents->Report (
  840. EVENT_FDEPLOY_FOLDER_OFFLINE,
  841. 3,
  842. m_szLocDisplayName,
  843. wszExpandedCurrentPath,
  844. wszExpandedNewFolderPath
  845. );
  846. goto Redir_KillChildAndLeave;
  847. }
  848. }
  849. DebugMsg (( DM_VERBOSE, IDS_REDIRECT_PREVPATH,
  850. pLocalSettings->m_szCurrentPath,
  851. wszExpandedCurrentPath));
  852. DebugMsg (( DM_VERBOSE, IDS_REDIRECT_NEWPATH, m_szLocation,
  853. wszExpandedNewFolderPath));
  854. m_StatusRedir = PerformRedirection (
  855. pFileDB,
  856. bCurrentPathValid,
  857. wszExpandedCurrentPath,
  858. wszExpandedNewFolderPath,
  859. SourceStatus,
  860. DestStatus,
  861. hUserToken
  862. );
  863. if (ERROR_SUCCESS == m_StatusRedir)
  864. {
  865. if (m_bRemove)
  866. {
  867. if (MyDocs == m_rID)
  868. RestrictMyDocsRedirection (hUserToken, hKeyRoot, FALSE);
  869. pLocalSettings->Save (m_szLocation, REDIR_DONT_CARE, NULL, NULL);
  870. }
  871. else
  872. {
  873. if (MyDocs == m_rID)
  874. RestrictMyDocsRedirection (hUserToken, hKeyRoot,
  875. (m_dwFlags & REDIR_DONT_CARE) ? FALSE : TRUE);
  876. pLocalSettings->Save (m_szLocation, m_dwFlags, m_pSid, m_bValidGPO ? m_szGPOName : 0);
  877. }
  878. }
  879. else if (m_pChild && m_pChild->m_bFollowsParent) //if this is a parent whose redirection was unsuccessful, do not redirect the child if it is supposed to follow
  880. {
  881. PreventDescendantRedirection (m_StatusRedir);
  882. }
  883. goto Redir_CleanupAndQuit;
  884. }
  885. //if we are here, it means that the saved settings don't have the
  886. //REDIR_DONT_CARE flag set
  887. if (m_dwFlags & REDIR_DONT_CARE)
  888. {
  889. //the original settings cared about the location of the folder,
  890. //but now no one cares. So remove any redirection restrictions.
  891. if (MyDocs == m_rID)
  892. RestrictMyDocsRedirection (hUserToken, hKeyRoot, FALSE);
  893. pLocalSettings->Save (pLocalSettings->m_szCurrentPath, m_dwFlags, NULL, NULL);
  894. m_StatusRedir = ERROR_SUCCESS;
  895. goto Redir_CleanupAndQuit;
  896. }
  897. DebugMsg((DM_VERBOSE, IDS_REDIRECT, m_szLocDisplayName, m_szLocation));
  898. // This policy cares about where this folder goes.
  899. //
  900. // So first expand the homedir part if applicable
  901. // use the last value of homedir if the value of homedir has changed.
  902. //
  903. m_StatusRedir = ExpandHomeDir(m_rID,
  904. pLocalSettings->m_szLastRedirectedPath,
  905. TRUE,
  906. &wszProcessedPath,
  907. pLocalSettings->m_bHomedirChanged ? pLocalSettings->m_szLastHomedir : NULL);
  908. if (ERROR_SUCCESS == m_StatusRedir)
  909. {
  910. // Now first expand the saved path, we will use that for redirection
  911. Path.Length = (wcslen (wszProcessedPath) + 1) * sizeof (WCHAR);
  912. Path.MaximumLength = Path.Length;
  913. Path.Buffer = wszProcessedPath;
  914. ExpandedPath.Length = 0;
  915. ExpandedPath.MaximumLength = sizeof (wszExpandedSavedFolderPath);
  916. ExpandedPath.Buffer = wszExpandedSavedFolderPath;
  917. m_StatusRedir = RtlExpandEnvironmentStrings_U (
  918. pFileDB->_pEnvBlock,
  919. &Path,
  920. &ExpandedPath,
  921. NULL
  922. );
  923. if (STATUS_BUFFER_TOO_SMALL == m_StatusRedir)
  924. {
  925. gpEvents->Report (
  926. EVENT_FDEPLOY_DESTPATH_TOO_LONG,
  927. 3,
  928. m_szLocDisplayName,
  929. pLocalSettings->m_szLastRedirectedPath,
  930. NumberToString ( TARGETPATHLIMIT )
  931. );
  932. goto Redir_KillChildAndLeave;
  933. }
  934. }
  935. if (ERROR_SUCCESS != m_StatusRedir)
  936. {
  937. DebugMsg ((DM_WARNING, IDS_REDIRECT_EXP_FAIL, pLocalSettings->m_szLastRedirectedPath, m_StatusRedir));
  938. gpEvents->Report (
  939. EVENT_FDEPLOY_FOLDER_EXPAND_FAIL,
  940. 2,
  941. m_szLocDisplayName,
  942. StatusToString ( m_StatusRedir )
  943. );
  944. goto Redir_KillChildAndLeave;
  945. }
  946. //make sure that it is not already redirected to the location.
  947. if (0 == _wcsicmp (wszExpandedSavedFolderPath, wszExpandedNewFolderPath))
  948. {
  949. //the cached path and the path specified by the policy is the same
  950. //but someone may have messed with the registry, if that is the case
  951. //we use the path in the registry as the base path
  952. //must get the correct syntax for GetLocalFilePath
  953. lstrcpy (wszHackedName, m_szFolderRelativePath);
  954. lstrcat (wszHackedName, L"\\");
  955. m_StatusRedir = pFileDB->GetLocalFilePath (wszHackedName, wszExpandedCurrentPath);
  956. //
  957. // Expand the homedir if necessary. Note: GetLocalFilePath will not
  958. // do it because it calls SHGetFolderPath and HOMESHARE and HOMEPATH
  959. // are not defined at that point.
  960. //
  961. if (ERROR_SUCCESS == m_StatusRedir)
  962. {
  963. m_StatusRedir = ExpandHomeDir (m_rID,
  964. wszExpandedCurrentPath,
  965. TRUE,
  966. &wszProcessedPath,
  967. pLocalSettings->m_bHomedirChanged ? pLocalSettings->m_szLastHomedir : NULL
  968. );
  969. if (ERROR_SUCCESS != m_StatusRedir ||
  970. ! wszProcessedPath ||
  971. TARGETPATHLIMIT <= lstrlen (wszProcessedPath))
  972. {
  973. m_StatusRedir = (ERROR_SUCCESS == m_StatusRedir) ? ERROR_BUFFER_OVERFLOW : m_StatusRedir;
  974. }
  975. else
  976. {
  977. lstrcpy (wszExpandedCurrentPath, wszProcessedPath);
  978. }
  979. }
  980. //GetLocalFilePath may fail if SHGetFolderPath fails. This
  981. //would mean that the registry is messed up anyway. Also, move contents
  982. //no longer makes sense, since the path to which the reg. points to
  983. //is invalid or worse, the registry key is missing. also, it would
  984. //imply that there is nothing in the CSC cache
  985. if ((ERROR_SUCCESS != m_StatusRedir) &&
  986. ERROR_INVALID_NAME != m_StatusRedir) //the only possible error from GetLocalFilePath not generated by SHGetFolderPath
  987. {
  988. m_StatusRedir = ERROR_SUCCESS;
  989. bCurrentPathValid = FALSE;
  990. m_dwFlags &= ~REDIR_MOVE_CONTENTS;
  991. }
  992. if (m_StatusRedir != ERROR_SUCCESS)
  993. {
  994. DebugMsg ((DM_WARNING, IDS_REDIRECT_NO_LOCAL, m_szLocDisplayName, m_StatusRedir));
  995. gpEvents->Report (
  996. EVENT_FDEPLOY_FOLDER_EXPAND_FAIL,
  997. 2,
  998. m_szLocDisplayName,
  999. StatusToString ( m_StatusRedir )
  1000. );
  1001. goto Redir_KillChildAndLeave;
  1002. }
  1003. //we have found out the path stored in the registry, so compare it with
  1004. //the saved path
  1005. if (bCurrentPathValid && (0 == _wcsicmp (wszExpandedSavedFolderPath, wszExpandedCurrentPath)))
  1006. {
  1007. //all the paths are identical, so we are fine. save and quit
  1008. pLocalSettings->Save (m_szLocation, m_dwFlags, m_pSid, m_bValidGPO ? m_szGPOName : 0);
  1009. if (MyDocs == m_rID)
  1010. RestrictMyDocsRedirection (hUserToken, hKeyRoot,
  1011. (m_bRemove || (m_dwFlags & REDIR_DONT_CARE))?FALSE:TRUE);
  1012. DebugMsg ((DM_VERBOSE, IDS_REDIRECT_INSYNC, m_szLocDisplayName,
  1013. wszExpandedNewFolderPath));
  1014. //however, it is possible that the folders have not been pinned.
  1015. //e.g. a roaming user who has already been redirected on one machine
  1016. //and now logs on to another machine for the first time.
  1017. DestStatus = GetCSCStatus (wszExpandedNewFolderPath);
  1018. if (ShareOnline == DestStatus)
  1019. {
  1020. PinStatus = PinIfNecessary (wszExpandedNewFolderPath, DestStatus);
  1021. if ( ERROR_SUCCESS != PinStatus )
  1022. DebugMsg((DM_VERBOSE, IDS_CSCPIN_FAIL,
  1023. m_szLocDisplayName, PinStatus));
  1024. CacheDesktopIni (wszExpandedNewFolderPath, DestStatus, PinFile);
  1025. }
  1026. m_StatusRedir = ERROR_SUCCESS;
  1027. goto Redir_CleanupAndQuit;
  1028. }
  1029. else //somebody has been messing with the registry
  1030. {
  1031. //use the path in the registry as the source path
  1032. //and perform redirection again
  1033. if (bCurrentPathValid)
  1034. wcscpy (wszExpandedSavedFolderPath, wszExpandedCurrentPath);
  1035. else
  1036. wcscpy(wszExpandedSavedFolderPath, L"???"); //to be on the safe side, since we don't want to use the saved path as the source for redirection
  1037. //if bCurrentPathValid is FALSE, this string will only be used in debug messages and error logs
  1038. }
  1039. }
  1040. if (g_bCSCEnabled)
  1041. {
  1042. SourceStatus = bCurrentPathValid ? GetCSCStatus (wszExpandedSavedFolderPath) : PathLocal;
  1043. DestStatus = GetCSCStatus (wszExpandedNewFolderPath);
  1044. if (ShareOffline == DestStatus ||
  1045. ((m_dwFlags & REDIR_MOVE_CONTENTS) && ShareOffline == SourceStatus))
  1046. {
  1047. m_StatusRedir = ERROR_CSCSHARE_OFFLINE;
  1048. gpEvents->Report (
  1049. EVENT_FDEPLOY_FOLDER_OFFLINE,
  1050. 3,
  1051. m_szLocDisplayName,
  1052. wszExpandedSavedFolderPath,
  1053. wszExpandedNewFolderPath
  1054. );
  1055. goto Redir_KillChildAndLeave;
  1056. }
  1057. }
  1058. DebugMsg (( DM_VERBOSE, IDS_REDIRECT_PREVPATH,
  1059. pLocalSettings->m_szLastRedirectedPath,
  1060. wszExpandedSavedFolderPath));
  1061. DebugMsg (( DM_VERBOSE, IDS_REDIRECT_NEWPATH, m_szLocation,
  1062. wszExpandedNewFolderPath));
  1063. m_StatusRedir = PerformRedirection (
  1064. pFileDB,
  1065. bCurrentPathValid,
  1066. wszExpandedSavedFolderPath,
  1067. wszExpandedNewFolderPath,
  1068. SourceStatus,
  1069. DestStatus,
  1070. hUserToken
  1071. );
  1072. if (ERROR_SUCCESS == m_StatusRedir)
  1073. {
  1074. if (m_bRemove)
  1075. {
  1076. if (MyDocs == m_rID)
  1077. RestrictMyDocsRedirection (hUserToken, hKeyRoot, FALSE);
  1078. pLocalSettings->Save (m_szLocation, REDIR_DONT_CARE, NULL, NULL);
  1079. }
  1080. else
  1081. {
  1082. if (MyDocs == m_rID)
  1083. RestrictMyDocsRedirection (hUserToken, hKeyRoot,
  1084. (m_dwFlags & REDIR_DONT_CARE)?FALSE:TRUE);
  1085. pLocalSettings->Save (m_szLocation, m_dwFlags, m_pSid, m_bValidGPO ? m_szGPOName : 0);
  1086. }
  1087. }
  1088. else if (m_pChild && m_pChild->m_bFollowsParent) //if redirection was unsuccessful and this folder has children, prevent the redirection of the children if they are supposed to follow
  1089. {
  1090. PreventDescendantRedirection (m_StatusRedir);
  1091. }
  1092. goto Redir_CleanupAndQuit;
  1093. //the following code is executed whenever some fatal error occurs
  1094. //and we want to make sure that if this is a dir with a special
  1095. //descendant and the descendant is supposed to follow the parent,
  1096. //then we don't want to attempt redirection for the child.
  1097. Redir_KillChildAndLeave:
  1098. if (ERROR_SUCCESS != m_StatusRedir &&
  1099. (m_pChild && m_pChild->m_bFollowsParent))
  1100. PreventDescendantRedirection (m_StatusRedir);
  1101. Redir_CleanupAndQuit:
  1102. if (wszProcessedPath)
  1103. delete [] wszProcessedPath;
  1104. if ( ERROR_SUCCESS != m_StatusRedir )
  1105. {
  1106. HRESULT hrRsop;
  1107. hrRsop = pFileDB->AddPreservedPolicy( (WCHAR*) m_szDisplayName );
  1108. if ( FAILED( hrRsop ) )
  1109. {
  1110. pFileDB->GetRsopContext()->DisableRsop( hrRsop );
  1111. }
  1112. }
  1113. return m_StatusRedir;
  1114. }
  1115. //+--------------------------------------------------------------------------
  1116. //
  1117. // Member: CRedirectInfo::ShouldSaveExpandedPath
  1118. //
  1119. // Synopsis: Determines whether we need to store the expanded path in the
  1120. // registry or the unexpanded paths. See notes for additional
  1121. // details.
  1122. //
  1123. // Arguments: none.
  1124. //
  1125. // Returns: TRUE : if the expanded path should be stored.
  1126. // FALSE : otherwise.
  1127. //
  1128. // History: 5/1/2001 RahulTh created
  1129. //
  1130. // Notes:
  1131. // If the destination path is a homedir path, store the expanded path
  1132. // in the registry so that if msgina is unable to set the homedir
  1133. // variables and uses the local defaults instead, the shell doesn't
  1134. // end up thinking that MyDocs is local.
  1135. //
  1136. // If the destination path is a different kind of path, then store the
  1137. // expanded path if it has the %username% variable in it. Because if we
  1138. // don't then when a user's UPN changes and the user logs on with the new
  1139. // username, then the shell will expand the path using the new username.
  1140. // Now, normally this won't be a problem because we would have already
  1141. // done the rename operation. However, if for some reason we are
  1142. // not successful in that rename operation (say because we are
  1143. // invoked in limited foreground or because the server is offline)
  1144. // then the rename will not succeed. In this case, the shell will
  1145. // end up creating a new empty folder in that location whenever the
  1146. // user tries to access it. So the next time the user logs on,
  1147. // the new folder is already present and the rename fails with
  1148. // ERROR_ALREADY_EXISTS and we just end up pointing to the new
  1149. // location. The files stay in the old location. Therefore, we must
  1150. // not use the unexpanded path in SHSetFolderPath. However, we must
  1151. // store the unexpanded path in our local cache in order to
  1152. // successfully detect UPN changes.
  1153. //
  1154. // We do not want to store the expanded path unconditionally because
  1155. // in situations where the user is going back to a local location,
  1156. // especially the local userprofile location, we do not want to store
  1157. // the expanded path because it is not device independent and will cause
  1158. // problems for roaming users (e.g. a certain drive may not be available
  1159. // on all the machines so we should keep the path as %userprofile%\...
  1160. // rather than something like E:\Documents & Settings\...)
  1161. //
  1162. //---------------------------------------------------------------------------
  1163. BOOL CRedirectInfo::ShouldSaveExpandedPath(void)
  1164. {
  1165. if (! m_szLocation || L'\0' == m_szLocation[0])
  1166. return FALSE;
  1167. // Detect the homedir case
  1168. if (IsHomedirPolicyPath(m_rID, m_szLocation, TRUE))
  1169. return TRUE;
  1170. //
  1171. // Check if the path contains the username variable.
  1172. // If it does then we should store the expanded paths. However regardless
  1173. // of everything else, if the path begins with %userprofile%, we should
  1174. // never store the expanded path because we will never be guaranteed a
  1175. // device independent path in that case and will run into all sorts of
  1176. // problems.
  1177. //
  1178. _wcslwr (m_szLocation);
  1179. // Compare the beginning of the two strings (the -1 is required to prevent comparing the terminating NULL)
  1180. if (0 == wcsncmp (m_szLocation, L"%userprofile%", sizeof(L"%userprofile%")/sizeof(WCHAR) - 1))
  1181. return FALSE;
  1182. if (wcsstr (m_szLocation, L"%username%"))
  1183. return TRUE;
  1184. //
  1185. // If we are here, we did not meet any of the conditions required for
  1186. // storing the expanded paths. So we should just store the unexpanded paths
  1187. //
  1188. return FALSE;
  1189. }
  1190. //+--------------------------------------------------------------------------
  1191. //
  1192. // Member: CRedirectInfo::PerformRedirection
  1193. //
  1194. // Synopsis: performs the nitty gritty of redirection including copying
  1195. // files, updating the registry, logging events etc.
  1196. //
  1197. // Arguments: [in] pFileDB : the CFileDB structure that is used throughout
  1198. // [in] bSourceValid : if pwszSource contains a valid path
  1199. // [in] pwszSource : source path
  1200. // [in] pwszDest : Destination path
  1201. // [in] StatusFrom : CSC status of the source share
  1202. // [in] StatusTo : CSC status of the destination share
  1203. //
  1204. // Returns: ERROR_SUCCESS if everything was successful. An error code
  1205. // otherwise.
  1206. //
  1207. // History: 11/21/1998 RahulTh created
  1208. // 12/13/2000 RahulTh Special cased homedir redirection to
  1209. // prevent security checks and store expanded
  1210. // paths in the registry (see comments within the function)
  1211. //
  1212. // Notes: this functions expects both shares to be online when it is
  1213. // invoked.
  1214. //
  1215. //---------------------------------------------------------------------------
  1216. DWORD CRedirectInfo::PerformRedirection (CFileDB * pFileDB,
  1217. BOOL bSourceValid,
  1218. WCHAR * pwszSource,
  1219. WCHAR * pwszDest,
  1220. SHARESTATUS StatusFrom,
  1221. SHARESTATUS StatusTo,
  1222. HANDLE hUserToken
  1223. )
  1224. {
  1225. BOOL bCheckOwner;
  1226. BOOL bMoveContents;
  1227. BOOL bIsHomeDirRedirection = FALSE; // Tracks if this is a homedir redirection policy
  1228. DWORD Status;
  1229. int iResultCompare;
  1230. WCHAR * pwszSkipSubdir;
  1231. int csidl;
  1232. BOOL bStatus;
  1233. DWORD PinStatus;
  1234. HRESULT hResult = S_OK;
  1235. CCopyFailData CopyFailure;
  1236. //
  1237. // We need to track if this is a homedir redirection policy because
  1238. // of 2 reasons:
  1239. // 1) In homedir redirection, security checks are skipped.
  1240. // 2) In homedir redirection, the expanded path is stored in the registry. This is
  1241. // to prevent problems that might occur if the homedir variables cannot be
  1242. // set by msgina and end up pointing to the local userprofile. In this case,
  1243. // we do not want the shell to start pointing MyDocs to the local location.
  1244. //
  1245. bIsHomeDirRedirection = IsHomedirPolicyPath(m_rID, m_szLocation, TRUE);
  1246. csidl = pFileDB->RegValueCSIDLFromFolderName (m_szFolderRelativePath);
  1247. // Skip security check for homedir redirection.
  1248. bCheckOwner = ((m_dwFlags & REDIR_SETACLS) && (!bIsHomeDirRedirection)) ? TRUE : FALSE;
  1249. bMoveContents = m_dwFlags & REDIR_MOVE_CONTENTS ? TRUE : FALSE;
  1250. if (bSourceValid)
  1251. {
  1252. Status = ComparePaths (pwszSource, pwszDest, &iResultCompare);
  1253. if (ERROR_SUCCESS != Status)
  1254. {
  1255. //this is very unlikely
  1256. gpEvents->Report (
  1257. EVENT_FDEPLOY_REDIRECT_FAIL,
  1258. 4,
  1259. m_szLocDisplayName,
  1260. StatusToString (Status),
  1261. pwszSource,
  1262. pwszDest
  1263. );
  1264. return Status;
  1265. }
  1266. if (0 == iResultCompare)
  1267. {
  1268. DebugMsg ((DM_VERBOSE, IDS_REDIRECT_INSYNC, m_szLocDisplayName,
  1269. pwszDest));
  1270. //however, it is possible that the folders have not been pinned.
  1271. //e.g. a roaming user who has already been redirected on one machine
  1272. //and now logs on to another machine for the first time.
  1273. if (ShareOnline == StatusTo)
  1274. {
  1275. PinStatus = PinIfNecessary (pwszDest, StatusTo);
  1276. if ( ERROR_SUCCESS != PinStatus )
  1277. DebugMsg((DM_VERBOSE, IDS_CSCPIN_FAIL,
  1278. m_szLocDisplayName, PinStatus));
  1279. CacheDesktopIni (pwszDest, StatusTo, PinFile);
  1280. }
  1281. return ERROR_SUCCESS;
  1282. }
  1283. //it is okay to redirect to a path that is a descendant of the current
  1284. //path if we are not moving contents
  1285. if (bMoveContents && (-1 == iResultCompare))
  1286. {
  1287. gpEvents->Report (
  1288. EVENT_FDEPLOY_REDIRECT_RECURSE,
  1289. 4,
  1290. m_szLocDisplayName,
  1291. m_szLocation,
  1292. pwszSource,
  1293. pwszDest
  1294. );
  1295. return ERROR_REQUEST_ABORTED;
  1296. }
  1297. }
  1298. Status = pFileDB->CreateRedirectedFolderPath (pwszSource, pwszDest,
  1299. bSourceValid,
  1300. bCheckOwner, bMoveContents);
  1301. if (ERROR_SUCCESS != Status)
  1302. {
  1303. gpEvents->Report (
  1304. EVENT_FDEPLOY_FOLDER_CREATE_FAIL,
  1305. 4,
  1306. m_szLocDisplayName,
  1307. StatusToString (Status),
  1308. m_szLocation,
  1309. pwszDest
  1310. );
  1311. return Status;
  1312. }
  1313. //now we know that both the source and destinations paths exist
  1314. //so make sure that they are not the same path in different formats
  1315. //this is an expensive function as it involves creation and deletion of
  1316. //multiple files over the network. so we invoke it only if absolutely
  1317. //necessary
  1318. if (bSourceValid && bMoveContents)
  1319. {
  1320. Status = CheckIdenticalSpecial (pwszSource, pwszDest, &iResultCompare);
  1321. if (ERROR_SUCCESS != Status)
  1322. {
  1323. //this is very unlikely...
  1324. gpEvents->Report (
  1325. EVENT_FDEPLOY_REDIRECT_FAIL,
  1326. 4,
  1327. m_szLocDisplayName,
  1328. StatusToString (Status),
  1329. pwszSource,
  1330. pwszDest
  1331. );
  1332. return Status;
  1333. }
  1334. if (0 == iResultCompare)
  1335. {
  1336. DebugMsg ((DM_VERBOSE, IDS_REDIRECT_INSYNC, m_szLocDisplayName,
  1337. pwszDest));
  1338. //
  1339. // The paths are the same but in different formats (or perhaps
  1340. // through different shares. So at least update the registry to
  1341. // point to the new path.
  1342. //
  1343. hResult = SHSetFolderPath(csidl | CSIDL_FLAG_DONT_UNEXPAND,
  1344. hUserToken,
  1345. 0,
  1346. ShouldSaveExpandedPath() ? pwszDest : m_szLocation);
  1347. Status = GetWin32ErrFromHResult (hResult);
  1348. //
  1349. // This basically should never fail. But do we want to try to delete the
  1350. // copied files if it does? Or the wacked CSC database?
  1351. //
  1352. if ( Status != ERROR_SUCCESS )
  1353. {
  1354. gpEvents->Report(
  1355. EVENT_FDEPLOY_FOLDER_REGSET_FAIL,
  1356. 2,
  1357. m_szLocDisplayName,
  1358. StatusToString( Status ) );
  1359. return Status;
  1360. }
  1361. else
  1362. {
  1363. //we were successul.
  1364. //first, rename the local CSC cache.
  1365. if (m_pChild)
  1366. pwszSkipSubdir = m_pChild->m_szLocDisplayName;
  1367. else
  1368. pwszSkipSubdir = NULL;
  1369. MoveDirInCSC (pwszSource, pwszDest, pwszSkipSubdir,
  1370. StatusFrom, StatusTo, TRUE, TRUE);
  1371. if (g_bCSCEnabled && ShareOnline == StatusFrom)
  1372. {
  1373. DeleteCSCFileTree (pwszSource, pwszSkipSubdir, TRUE);
  1374. DeleteCSCShareIfEmpty (pwszSource, StatusFrom);
  1375. }
  1376. //Also, it is possible that the folders have not been pinned.
  1377. //e.g. a roaming user who has already been redirected on one
  1378. //machine and now logs on to another machine for the first time.
  1379. if (ShareOnline == StatusTo)
  1380. {
  1381. PinStatus = PinIfNecessary (pwszDest, StatusTo);
  1382. if ( ERROR_SUCCESS != PinStatus )
  1383. DebugMsg((DM_VERBOSE, IDS_CSCPIN_FAIL,
  1384. m_szLocDisplayName, PinStatus));
  1385. CacheDesktopIni (pwszDest, StatusTo, PinFile);
  1386. }
  1387. //report this event and return. there is
  1388. //nothing more to be done here.
  1389. gpEvents->Report(
  1390. EVENT_FDEPLOY_FOLDER_REDIRECT,
  1391. 3,
  1392. m_szLocDisplayName,
  1393. bSourceValid ? pwszSource : L"???",
  1394. pwszDest );
  1395. return ERROR_SUCCESS;
  1396. }
  1397. }
  1398. }
  1399. if (bSourceValid && bMoveContents)
  1400. {
  1401. DebugMsg ((DM_VERBOSE, IDS_REDIRECT_COPYON, m_szLocDisplayName));
  1402. //
  1403. // Exclude any special descendants when
  1404. // doing file copies and deletes. Similarly for Programs and
  1405. // Startup
  1406. //
  1407. if (m_pChild)
  1408. pwszSkipSubdir = m_pChild->m_szLocDisplayName;
  1409. else
  1410. pwszSkipSubdir = NULL;
  1411. Status = pFileDB->CopyFileTree( pwszSource,
  1412. pwszDest,
  1413. pwszSkipSubdir,
  1414. StatusFrom,
  1415. StatusTo,
  1416. TRUE,
  1417. &CopyFailure );
  1418. //it is necessary to do the following for 2 reasons:
  1419. //(a) the user may have dirty files in the cache. these don't get
  1420. // moved above.
  1421. //(b) the files may have already been moved on the network by the
  1422. // the extension when the user logged on from another machine
  1423. // therefore, the cache on the second machine never gets updated
  1424. //
  1425. //we only try our best to move the files here. Errors are ignored.
  1426. if (ERROR_SUCCESS == Status)
  1427. {
  1428. MoveDirInCSC (pwszSource, pwszDest,
  1429. pwszSkipSubdir,
  1430. StatusFrom, StatusTo, FALSE, TRUE);
  1431. }
  1432. else
  1433. {
  1434. //the copy failed. We might have failed halfway and left the cache
  1435. //in an inconsistent state, so rollback all the cached entries
  1436. MoveDirInCSC (pwszDest, pwszSource, pwszSkipSubdir,
  1437. StatusTo, StatusFrom, TRUE, TRUE);
  1438. }
  1439. if ( ERROR_SUCCESS != Status )
  1440. {
  1441. if (! CopyFailure.IsCopyFailure())
  1442. {
  1443. gpEvents->Report(
  1444. EVENT_FDEPLOY_FOLDER_MOVE_FAIL,
  1445. 5,
  1446. m_szLocDisplayName,
  1447. StatusToString( Status ),
  1448. m_szLocation,
  1449. pwszSource,
  1450. pwszDest );
  1451. }
  1452. else
  1453. {
  1454. gpEvents->Report(
  1455. EVENT_FDEPLOY_FOLDER_COPY_FAIL,
  1456. 7,
  1457. m_szLocDisplayName,
  1458. StatusToString( Status ),
  1459. m_szLocation,
  1460. pwszSource,
  1461. pwszDest,
  1462. CopyFailure.GetSourceName(),
  1463. CopyFailure.GetDestName() );
  1464. }
  1465. return Status;
  1466. }
  1467. }
  1468. else
  1469. {
  1470. DebugMsg((DM_VERBOSE, IDS_REDIRECT_COPYOFF, m_szLocDisplayName));
  1471. }
  1472. //
  1473. // Look at the comments for ShouldSaveExpandedPath() for details on why we
  1474. // sometimes need to store expanded paths.
  1475. //
  1476. hResult = SHSetFolderPath(csidl | CSIDL_FLAG_DONT_UNEXPAND,
  1477. hUserToken,
  1478. 0,
  1479. ShouldSaveExpandedPath() ? pwszDest : m_szLocation);
  1480. Status = GetWin32ErrFromHResult (hResult);
  1481. if ( Status != ERROR_SUCCESS )
  1482. {
  1483. gpEvents->Report(
  1484. EVENT_FDEPLOY_FOLDER_REGSET_FAIL,
  1485. 2,
  1486. m_szLocDisplayName,
  1487. StatusToString( Status ) );
  1488. return Status;
  1489. }
  1490. if (!m_bRemove)
  1491. {
  1492. //
  1493. // hack to work around a shell problem.
  1494. //
  1495. // Pin the folder so that the shell never gets an error when
  1496. // trying to resolve this path. This will prevent it from
  1497. // reverting to a temporary local path.
  1498. //
  1499. // For now just call pin/unpin APIs for any unc style path. Not
  1500. // really much value in checking first to see if the share is
  1501. // cacheable.
  1502. //
  1503. if ( bSourceValid &&
  1504. (L'\\' == pwszSource[0]) &&
  1505. (L'\\' == pwszSource[1]) )
  1506. {
  1507. CSCUnpinFile( pwszSource,
  1508. FLAG_CSC_HINT_COMMAND_ALTER_PIN_COUNT,
  1509. NULL, NULL, NULL );
  1510. CacheDesktopIni (pwszSource, StatusFrom, UnpinFile);
  1511. }
  1512. if ( (L'\\' == pwszDest[0]) &&
  1513. (L'\\' == pwszDest[1]) )
  1514. {
  1515. PinStatus = PinIfNecessary (pwszDest, StatusTo);
  1516. if ( ERROR_SUCCESS != PinStatus )
  1517. DebugMsg((DM_VERBOSE, IDS_CSCPIN_FAIL,
  1518. m_szLocDisplayName, PinStatus));
  1519. CacheDesktopIni (pwszDest, StatusTo, PinFile);
  1520. }
  1521. }
  1522. //the contents were moved. now redirect any special children
  1523. //this ensures that deletions (if any) in the child folders,
  1524. //are performed first. thus deletion of this folder won't fail
  1525. //due to existence of its children within it.
  1526. //should not check for m_pChild->m_bFollowsParent here as
  1527. //the child may currently lie under this folder and if we do
  1528. //not perform the redirection of the child here, we might have
  1529. //problems deleting this folder even when we should not have any
  1530. //problems
  1531. if (m_pChild)
  1532. {
  1533. Status = m_pChild->Redirect (pFileDB->_hUserToken,
  1534. pFileDB->_hkRoot, pFileDB);
  1535. }
  1536. //note : contents of the source should not be deleted if this is a
  1537. // policy removal
  1538. if ( bSourceValid && bMoveContents)
  1539. {
  1540. Status = ERROR_SUCCESS;
  1541. //leave the contents on the server if this is a policy removal.
  1542. //also leave the contents on the server when moving from
  1543. //a network to a local location, so that subsequent redirections
  1544. //from other workstations will get all the contents back to local.
  1545. if (!m_bRemove && (!IsPathLocal(pwszDest) || IsPathLocal(pwszSource)))
  1546. {
  1547. //
  1548. // This could fail because of ACLing. We ignore any failures here.
  1549. //
  1550. DebugMsg((DM_VERBOSE, IDS_REDIRECT_DELETE,
  1551. m_szLocDisplayName, pwszSource));
  1552. Status = pFileDB->DeleteFileTree( pwszSource,
  1553. pwszSkipSubdir
  1554. );
  1555. if ( ERROR_SUCCESS == Status )
  1556. {
  1557. //DeleteFileTree does not remove the top level node passed to it.
  1558. // Delete the top level node only if it is not the user's home
  1559. // directory
  1560. const WCHAR * pwszHomeDir = NULL;
  1561. DWORD dwHomeDirStatus = ERROR_SUCCESS;
  1562. pwszHomeDir = gUserInfo.GetHomeDir(dwHomeDirStatus);
  1563. if (NULL == pwszHomeDir || 0 != lstrcmpi (pwszHomeDir, pwszSource))
  1564. {
  1565. //clear the attributes before deleting.
  1566. SetFileAttributes (pwszSource,
  1567. FILE_ATTRIBUTE_NORMAL);
  1568. if ( ! RemoveDirectory( pwszSource ) )
  1569. {
  1570. Status = GetLastError();
  1571. DebugMsg((DM_VERBOSE, IDS_DIRDEL_FAIL,
  1572. pwszSource, Status));
  1573. }
  1574. }
  1575. }
  1576. if ( Status != ERROR_SUCCESS )
  1577. DebugMsg((DM_WARNING, IDS_REDIRECT_DEL_FAIL,
  1578. pwszSource, m_szLocDisplayName,
  1579. m_szLocation, Status));
  1580. }
  1581. //but we always clean up the CSC cache irrespective of whether it is a
  1582. //policy removal or not because folder redirection should be as transparent
  1583. //to the user as possible and it would be annoying for the user to get
  1584. //CSC notifications for shares that are no longer used as redirection
  1585. //targets.
  1586. if (g_bCSCEnabled && ShareOnline == StatusFrom)
  1587. {
  1588. DeleteCSCFileTree (pwszSource, pwszSkipSubdir, TRUE);
  1589. DeleteCSCShareIfEmpty (pwszSource, StatusFrom);
  1590. }
  1591. }
  1592. gpEvents->Report(
  1593. EVENT_FDEPLOY_FOLDER_REDIRECT,
  1594. 3,
  1595. m_szLocDisplayName,
  1596. bSourceValid ? pwszSource : L"???",
  1597. pwszDest );
  1598. return ERROR_SUCCESS;
  1599. }
  1600. //+--------------------------------------------------------------------------
  1601. //
  1602. // Member: PreventRedirection
  1603. //
  1604. // Synopsis: this function prevents the redirection code from attempting
  1605. // redirection. Also prevents redirection of any of the child
  1606. // folders.
  1607. //
  1608. // Arguments: [in] Status : the error code indicating the cause of failure.
  1609. //
  1610. // Returns: nothing. It will always succeed.
  1611. //
  1612. // History: 9/20/1999 RahulTh created
  1613. //
  1614. // Notes: if the pre-processing step that handles the user name change
  1615. // fails for some reason, this function is invoked so that
  1616. // any attempt at applying simultaneous policy changes is thwarted
  1617. //
  1618. //---------------------------------------------------------------------------
  1619. void CRedirectInfo::PreventRedirection (DWORD Status)
  1620. {
  1621. m_bRedirectionAttempted = TRUE;
  1622. m_StatusRedir = Status;
  1623. if (m_pChild)
  1624. PreventDescendantRedirection (Status);
  1625. return;
  1626. }
  1627. //+--------------------------------------------------------------------------
  1628. //
  1629. // Member: PreventDescendantRedirection
  1630. //
  1631. // Synopsis: this function invalidates the data in the children so that
  1632. // the client extension will not try to redirect them
  1633. // this is necessary to prevent redirection of children if the
  1634. // redirection of the parents failed.
  1635. //
  1636. // Arguments: [in] Status : the error code indicating the cause of failure
  1637. //
  1638. // Returns: nothing. it will always succeed.
  1639. //
  1640. // History: 11/21/1998 RahulTh created
  1641. //
  1642. // Notes:
  1643. //
  1644. //---------------------------------------------------------------------------
  1645. void CRedirectInfo::PreventDescendantRedirection (DWORD Status)
  1646. {
  1647. if (! m_pChild) //nothing to do if this is not a parent
  1648. return;
  1649. m_pChild->m_bRedirectionAttempted = TRUE;
  1650. m_pChild->m_StatusRedir = Status;
  1651. //disable Startup too if start menu failed.
  1652. if (StartMenu == m_rID)
  1653. {
  1654. m_pChild->m_pChild->m_bRedirectionAttempted = TRUE;
  1655. m_pChild->m_pChild->m_StatusRedir = Status;
  1656. }
  1657. return;
  1658. }
  1659. //+--------------------------------------------------------------------------
  1660. //
  1661. // Member: CRedirectInfo::UpdateDescendant
  1662. //
  1663. // Synopsis: fills up the internal variables of a descendant object
  1664. // if it is supposed to follow the parent by using the
  1665. // data stored in the parent object
  1666. //
  1667. // Arguments: none
  1668. //
  1669. // Returns: nothing
  1670. //
  1671. // History: 10/6/1998 RahulTh created
  1672. //
  1673. // Notes:
  1674. //
  1675. //---------------------------------------------------------------------------
  1676. void CRedirectInfo::UpdateDescendant (void)
  1677. {
  1678. DWORD len;
  1679. WCHAR * szLoc = NULL;
  1680. if (!m_pParent) //this is not a special descendant
  1681. goto UpdateEnd;
  1682. if (!(m_dwFlags & REDIR_FOLLOW_PARENT))
  1683. goto UpdateEnd; //nothing to do. this descendant is not supposed to follow the parent
  1684. if (!m_pParent->m_fDataValid || (m_pParent->m_dwFlags & REDIR_DONT_CARE))
  1685. {
  1686. m_fDataValid = m_pParent->m_fDataValid;
  1687. m_dwFlags = REDIR_DONT_CARE;
  1688. goto UpdateEnd;
  1689. }
  1690. m_fDataValid = m_pParent->m_fDataValid;
  1691. m_bFollowsParent = TRUE;
  1692. //if we are here, policy has been specified for the parent
  1693. len = lstrlen (m_szLocDisplayName) + lstrlen (m_pParent->m_szLocation) + 2; //one extra for the backslash
  1694. if (m_cbLocSize < len)
  1695. {
  1696. //we need to allocate memory
  1697. szLoc = new WCHAR [len];
  1698. if (!szLoc)
  1699. {
  1700. //out of memory. cannot derive info. from parent. will have to ignore this GPO
  1701. DebugMsg((DM_VERBOSE, IDS_DERIVEINFO_ERROR, m_szLocDisplayName));
  1702. m_dwFlags = REDIR_DONT_CARE;
  1703. m_fDataValid = FALSE;
  1704. goto UpdateEnd;
  1705. }
  1706. if (m_cbLocSize)
  1707. delete [] m_szLocation;
  1708. m_szLocation = szLoc;
  1709. m_cbLocSize = len;
  1710. }
  1711. if (m_pSid)
  1712. delete [] ((BYTE*) m_pSid);
  1713. MySidCopy (&m_pSid, m_pParent->m_pSid);
  1714. //copy the data
  1715. //first get the settings
  1716. m_dwFlags = m_pParent->m_dwFlags & (REDIR_SETACLS | REDIR_MOVE_CONTENTS | REDIR_RELOCATEONREMOVE);
  1717. lstrcpy (m_szLocation, m_pParent->m_szLocation);
  1718. len = lstrlen (m_szLocation);
  1719. if (len > 0 && L'\\' != m_szLocation[len - 1])
  1720. lstrcat (m_szLocation, L"\\"); //add a \ only if the parent's path is not
  1721. //terminated with a \. Otherwise, we will
  1722. //end up with 2 \ in the child's path which
  1723. //will land SHGetFolderPath into trouble
  1724. //after the redirection is done.
  1725. lstrcat (m_szLocation, m_szLocDisplayName); //use the localized folder name
  1726. m_bRemove = m_pParent->m_bRemove;
  1727. UpdateEnd:
  1728. return;
  1729. }
  1730. //+--------------------------------------------------------------------------
  1731. //
  1732. // Member: CRedirectInfo::operator=
  1733. //
  1734. // Synopsis: overloaded assignment operator used for merging
  1735. //
  1736. // Arguments: standard
  1737. //
  1738. // Returns: standard
  1739. //
  1740. // History: 10/6/1998 RahulTh created
  1741. //
  1742. // Notes: DO NOT copy the values of m_bRedirectionAttempted and
  1743. // m_StatusRedir in this function.
  1744. //
  1745. //---------------------------------------------------------------------------
  1746. CRedirectInfo& CRedirectInfo::operator= (const CRedirectInfo& ri)
  1747. {
  1748. WCHAR * szLoc = 0;
  1749. DWORD Status;
  1750. PSID Sid;
  1751. ASSERT (m_rID == ri.m_rID);
  1752. if (!ri.m_fDataValid)
  1753. goto AssignEnd;
  1754. if ((ri.m_dwFlags & REDIR_FOLLOW_PARENT) && MyPics == m_rID)
  1755. {
  1756. m_fDataValid = ri.m_fDataValid;
  1757. m_dwFlags = REDIR_FOLLOW_PARENT;
  1758. m_bRemove = ri.m_bRemove;
  1759. if (m_bValidGPO = ri.m_bValidGPO) //note:this IS an assignment -- not a comparison
  1760. wcscpy (m_szGPOName, ri.m_szGPOName);
  1761. else
  1762. m_szGPOName[0] = L'\0';
  1763. goto AssignEnd;
  1764. }
  1765. else if ((ri.m_dwFlags & (REDIR_DONT_CARE | REDIR_FOLLOW_PARENT)))
  1766. {
  1767. //REDIR_FOLLOW_PARENT will be set only if UpdateDescendant ran out of memory
  1768. //in any case, we will have to ignore the policy
  1769. //note that we have to special case My Pics above because UpdateDescendant
  1770. //is called for My Pics after all the policies have been looked at
  1771. //thus it has not been called at this point yet.
  1772. //the reason we call UpdateDescendant for My Pictures after looking at
  1773. //all the policies because it is possible to specify "Follow My Docs"
  1774. //in one policy and specify the location of My Docs in another policy
  1775. if (!m_fDataValid)
  1776. {
  1777. m_fDataValid = ri.m_fDataValid;
  1778. m_dwFlags = REDIR_DONT_CARE;
  1779. m_bRemove = ri.m_bRemove;
  1780. if (m_bValidGPO = ri.m_bValidGPO) //note: this IS an assignment -- not a comparison
  1781. wcscpy (m_szGPOName, ri.m_szGPOName);
  1782. else
  1783. m_szGPOName[0] = L'\0';
  1784. }
  1785. goto AssignEnd; //ignore. no policy settings for the GPO being merged.
  1786. }
  1787. //note: in the following code... before modifying any of the data
  1788. //we must make sure that we can get memory for all of the members
  1789. //if we fail for even one of them and we have already changed the rest
  1790. //we can run into an inconsistent state. Therefore, we first allocate
  1791. //all the required memory and then actually proceed with the copy.
  1792. Sid = 0;
  1793. Status = MySidCopy (&Sid, ri.m_pSid);
  1794. if (ERROR_SUCCESS != Status)
  1795. {
  1796. DebugMsg ((DM_VERBOSE, IDS_MERGE_FAILURE, m_szLocDisplayName));
  1797. goto AssignEnd;
  1798. }
  1799. if (m_cbLocSize < ri.m_cbLocSize)
  1800. {
  1801. szLoc = new WCHAR [ri.m_cbLocSize];
  1802. if (!szLoc)
  1803. {
  1804. //we could not obtain memory to store the new path.
  1805. //we will have to ignore this policy
  1806. DebugMsg ((DM_VERBOSE, IDS_MERGE_FAILURE, m_szLocDisplayName));
  1807. delete [] ((BYTE*) Sid); //do not do this at the end. The same memory will be used by m_pSid.
  1808. goto AssignEnd;
  1809. }
  1810. if (m_cbLocSize) delete [] m_szLocation;
  1811. m_szLocation = szLoc;
  1812. m_cbLocSize = ri.m_cbLocSize;
  1813. }
  1814. //now we have the required memory, so we won't fail.
  1815. //fill in the data.
  1816. if (m_pSid)
  1817. delete [] ((BYTE*) m_pSid);
  1818. m_pSid = Sid;
  1819. lstrcpy (m_szLocation, ri.m_szLocation);
  1820. m_dwFlags = ri.m_dwFlags & (REDIR_SETACLS | REDIR_MOVE_CONTENTS | REDIR_RELOCATEONREMOVE);
  1821. m_bRemove = ri.m_bRemove;
  1822. m_bFollowsParent = ri.m_bFollowsParent;
  1823. m_fDataValid = ri.m_fDataValid;
  1824. if (m_bValidGPO = ri.m_bValidGPO) //note: this IS an assignment not a comparison
  1825. wcscpy (m_szGPOName, ri.m_szGPOName);
  1826. else
  1827. m_szGPOName[0] = L'\0';
  1828. AssignEnd:
  1829. return *this;
  1830. }
  1831. //+--------------------------------------------------------------------------
  1832. //
  1833. // Member: CRedirectInfo::ComputeEffectivePolicyRemoval
  1834. //
  1835. // Synopsis: tries to find out if the removal of a user from a group
  1836. // has caused a particular policy to be effectively removed
  1837. // for a particular user.
  1838. //
  1839. // Arguments: [pGPOList] : a list of GPOs still in effect for this user.
  1840. // if a GPO is effectively removed for this user, it
  1841. // has to figure in this list.
  1842. // [pFileDB] : pointer to the file DB structure
  1843. //
  1844. // Returns:
  1845. //
  1846. // History: 2/18/1999 RahulTh created
  1847. //
  1848. // Notes: this also detects cases where a user's group membership may
  1849. // not have changed but the policy no longer specifies any
  1850. // target for this group.
  1851. //
  1852. //---------------------------------------------------------------------------
  1853. DWORD CRedirectInfo::ComputeEffectivePolicyRemoval (
  1854. PGROUP_POLICY_OBJECT pDeletedGPOList,
  1855. PGROUP_POLICY_OBJECT pChangedGPOList,
  1856. CFileDB * pFileDB)
  1857. {
  1858. WCHAR pwszLocalPath[MAX_PATH];
  1859. WCHAR * pwszLocalIniFile = NULL;
  1860. PGROUP_POLICY_OBJECT pGPO;
  1861. WCHAR * pwszGPTIniFilePath = NULL;
  1862. DWORD Length = 0;
  1863. DWORD Status = ERROR_SUCCESS;
  1864. BOOL bStatus;
  1865. HANDLE hFind;
  1866. WIN32_FIND_DATA FindData;
  1867. WCHAR pwszDefault[] = L"*";
  1868. DWORD dwFlags;
  1869. WCHAR * pwszReturnedString = NULL;
  1870. DWORD Size = 0;
  1871. UNICODE_STRING StringW;
  1872. DWORD i;
  1873. PTOKEN_GROUPS pGroups;
  1874. WCHAR pwszSid[MAX_PATH]; //more than enough to store a sid.
  1875. BOOL bGPOInChangedList = FALSE;
  1876. //if the policy resultant is not set to DONT_CARE, it means that even if a
  1877. //policy has been effectively removed due to a group change or removal of a
  1878. //group from the advanced settings, some other policy has taken precedence
  1879. //and as a result, the policy resultant should remain the way it is.
  1880. if (! (m_dwFlags & REDIR_DONT_CARE))
  1881. {
  1882. Status = ERROR_SUCCESS;
  1883. goto CmpEffPolRem_End;
  1884. }
  1885. //there is no valid GPO stored in the per user per machine cache.
  1886. //so we cannot do much.
  1887. if (!gSavedSettings[m_rID].m_bValidGPO)
  1888. {
  1889. Status = ERROR_SUCCESS;
  1890. goto CmpEffPolRem_End;
  1891. }
  1892. //if the location of this folder was not specified by policy at last logon
  1893. //a group change cannot result in an effective policy removal for this folder.
  1894. if (gSavedSettings[m_rID].m_dwFlags & REDIR_DONT_CARE)
  1895. {
  1896. Status = ERROR_SUCCESS;
  1897. goto CmpEffPolRem_End;
  1898. }
  1899. //if we are here, then the folder was last redirected through policy and now
  1900. //either policy does not care, or a group change makes it seem so. If it is
  1901. //the latter, then we have to compute effective policy removal. Also, it is
  1902. //possible that the last policy application was a partial success and
  1903. //therefore a GPO that got deleted did not show up in either the changed
  1904. //GPO list or the deleted GPO list. We have to take into account that case
  1905. //too.
  1906. //first check if the GPO is present in the deleted GPO list.
  1907. for (pGPO = pDeletedGPOList; pGPO; pGPO = pGPO->pNext)
  1908. {
  1909. if (0 == _wcsicmp (gSavedSettings[m_rID].m_szGPOName, pGPO->szGPOName))
  1910. break;
  1911. }
  1912. if (!pGPO)
  1913. {
  1914. //if the policy isn't in the deleted GPO list, check if it is in the
  1915. //changed GPO list. If it isn't then this is a GPO that got deleted but did
  1916. //not show up in any of the lists because it never got fully applied.
  1917. //if it is, then either this is actually a don't care situation or it
  1918. //should be treated as policy removal because there was a group change.
  1919. for (pGPO = pChangedGPOList; pGPO; pGPO = pGPO->pNext)
  1920. {
  1921. if (0 == _wcsicmp (gSavedSettings[m_rID].m_szGPOName, pGPO->szGPOName))
  1922. break;
  1923. }
  1924. if (NULL != pGPO) //it is in the changed GPO list.
  1925. {
  1926. bGPOInChangedList = TRUE;
  1927. //get the path to the ini file on the sysvol.
  1928. Length = wcslen(pGPO->lpFileSysPath) + wcslen(GPT_SUBDIR) + wcslen (INIFILE_NAME) + 1;
  1929. pwszGPTIniFilePath = (WCHAR *) alloca( Length * sizeof(WCHAR) );
  1930. if ( ! pwszGPTIniFilePath )
  1931. {
  1932. Status = ERROR_OUTOFMEMORY;
  1933. goto CmpEffPolRem_End;
  1934. }
  1935. wcscpy( pwszGPTIniFilePath, pGPO->lpFileSysPath );
  1936. wcscat( pwszGPTIniFilePath, GPT_SUBDIR );
  1937. wcscat( pwszGPTIniFilePath, INIFILE_NAME );
  1938. }
  1939. }
  1940. //get the path to the locally cached copy of the ini file.
  1941. Length = wcslen (pFileDB->_pwszLocalPath) + wcslen (gSavedSettings[m_rID].m_szGPOName) + 6;
  1942. pwszLocalIniFile = (WCHAR *) alloca (Length * sizeof (WCHAR));
  1943. if (!pwszLocalIniFile)
  1944. {
  1945. Status = ERROR_OUTOFMEMORY;
  1946. goto CmpEffPolRem_End;
  1947. }
  1948. wcscpy( pwszLocalIniFile, pFileDB->_pwszLocalPath );
  1949. wcscat( pwszLocalIniFile, L"\\" );
  1950. wcscat( pwszLocalIniFile, gSavedSettings[m_rID].m_szGPOName );
  1951. wcscat( pwszLocalIniFile, L".ini" );
  1952. Status = ERROR_SUCCESS;
  1953. bStatus = FALSE;
  1954. if (bGPOInChangedList)
  1955. {
  1956. bStatus = CopyFile( pwszLocalIniFile, pwszGPTIniFilePath, FALSE );
  1957. }
  1958. if ( ! bStatus ) // Work off of the locally cached copy.
  1959. {
  1960. //try to use the cached version if any.
  1961. hFind = FindFirstFile( pwszLocalIniFile, &FindData );
  1962. if ( INVALID_HANDLE_VALUE != hFind )
  1963. {
  1964. Status = ERROR_SUCCESS;
  1965. FindClose( hFind );
  1966. }
  1967. else
  1968. {
  1969. Status = GetLastError();
  1970. }
  1971. }
  1972. //we don't have an ini file to work with, so we can't do much but quit.
  1973. if (ERROR_SUCCESS != Status)
  1974. goto CmpEffPolRem_End;
  1975. //now we have an ini file. so read the relevant info. from it.
  1976. //first grab the flags
  1977. Status = SafeGetPrivateProfileStringW (
  1978. L"FolderStatus",
  1979. m_szDisplayName,
  1980. pwszDefault,
  1981. &pwszReturnedString,
  1982. &Size,
  1983. pwszLocalIniFile
  1984. );
  1985. if (ERROR_SUCCESS != Status)
  1986. goto CmpEffPolRem_End;
  1987. if (L'*' == *pwszReturnedString)
  1988. {
  1989. //there are no settings for this folder. Possibly because
  1990. //someone changed the settings on the server.
  1991. //Treat it as a don't care.
  1992. goto CmpEffPolRem_End;
  1993. }
  1994. //now grab the hex flags
  1995. StringW.Buffer = pwszReturnedString;
  1996. StringW.Length = wcslen (pwszReturnedString) * sizeof (WCHAR);
  1997. StringW.MaximumLength = StringW.Length + sizeof(WCHAR);
  1998. RtlUnicodeStringToInteger( &StringW, 16, &dwFlags );
  1999. //if this is a special descendant folder and it is supposed to follow
  2000. //the parent, we might first have to derive its settings from the
  2001. //parent and then proceed.
  2002. if (m_pParent && (dwFlags & REDIR_FOLLOW_PARENT))
  2003. {
  2004. //the check for m_pParent is redundant since non-descendant folders
  2005. //can never have this flag. but this has been added as a safety mechanism
  2006. //against ini file corruption
  2007. //we will have to derive the settings from the parent later on.
  2008. m_dwFlags = REDIR_FOLLOW_PARENT;
  2009. m_fDataValid = TRUE;
  2010. m_bFollowsParent = TRUE;
  2011. m_bRemove = TRUE;
  2012. m_bValidGPO = FALSE; //since this is a removal
  2013. m_szGPOName[0] = L'\0';
  2014. UpdateDescendant(); //derive the settings from the parent
  2015. goto CmpEffPolRem_End;
  2016. }
  2017. if ((dwFlags & REDIR_DONT_CARE) ||
  2018. (m_bFollowsParent && (m_dwFlags & REDIR_DONT_CARE)))
  2019. {
  2020. //the policy has been changed to Don't Care. so it is not a removal.
  2021. //leave everything as is.
  2022. goto CmpEffPolRem_End;
  2023. }
  2024. if (!(dwFlags & REDIR_RELOCATEONREMOVE))
  2025. {
  2026. //the choice is to orphan. so let it stay as don't care.
  2027. goto CmpEffPolRem_End;
  2028. }
  2029. //
  2030. // If the GPO that was used for redirection the last time is not in the
  2031. // changed GPO list, then this is surely a policy removal.
  2032. // otherwise, it can either be a policy removal due to a group change (or
  2033. // a group's policy getting removed from the GPO or it can be just that
  2034. // the policy was changed to Don't care.
  2035. //
  2036. if (bGPOInChangedList)
  2037. {
  2038. //
  2039. // Check if the user is still a member of the group that was used for
  2040. // redirection.
  2041. //
  2042. pGroups = pFileDB->_pGroups;
  2043. for (i = 0, bStatus = FALSE;
  2044. i < pGroups->GroupCount && !bStatus;
  2045. i++
  2046. )
  2047. {
  2048. bStatus = RtlEqualSid (gSavedSettings[m_rID].m_psid, pGroups->Groups[i].Sid);
  2049. }
  2050. if (bStatus) //the user still belongs to that group.
  2051. {
  2052. //so perhaps the policy for this group was removed.
  2053. //make sure that this is the case.
  2054. Status = ERROR_INVALID_SID;
  2055. if (gSavedSettings[m_rID].m_psid)
  2056. {
  2057. pwszSid [0] = L'\0';
  2058. StringW.Length = 0;
  2059. StringW.MaximumLength = sizeof (pwszSid);
  2060. StringW.Buffer = pwszSid;
  2061. Status = RtlConvertSidToUnicodeString (&StringW, gSavedSettings[m_rID].m_psid, FALSE);
  2062. }
  2063. if (ERROR_SUCCESS != Status)
  2064. goto CmpEffPolRem_End;
  2065. Status = SafeGetPrivateProfileStringW (
  2066. m_szDisplayName,
  2067. StringW.Buffer,
  2068. pwszDefault,
  2069. &pwszReturnedString,
  2070. &Size,
  2071. pwszLocalIniFile
  2072. );
  2073. if (ERROR_SUCCESS != Status)
  2074. goto CmpEffPolRem_End;
  2075. if (0 != _wcsicmp(pwszReturnedString, pwszDefault))
  2076. {
  2077. //
  2078. // Policy exists for this folder so leave things the way they are.
  2079. // Ideally this is not possible and one should never enter this
  2080. // code path
  2081. //
  2082. goto CmpEffPolRem_End;
  2083. }
  2084. }
  2085. }
  2086. //
  2087. // If the user is no longer a member of the group, then this is clearly
  2088. // a case where the policy is effectively removed because the user was
  2089. // removed from a group that was used for redirection.
  2090. //
  2091. // At any rate, if we are here, then this is a policy removal so make the
  2092. // appropriate settings
  2093. //
  2094. if (m_cbLocSize && (m_cbLocSize < (Size = wcslen(L"%USERPROFILE%\\") + wcslen(m_szLocFolderRelativePath) + 1)))
  2095. {
  2096. delete [] m_szLocation;
  2097. m_cbLocSize = 0;
  2098. m_szLocation = new WCHAR [Size];
  2099. if (!m_szLocation)
  2100. {
  2101. Status = ERROR_OUTOFMEMORY;
  2102. goto CmpEffPolRem_End;
  2103. }
  2104. m_cbLocSize = Size;
  2105. }
  2106. wcscpy (m_szLocation, L"%USERPROFILE%\\");
  2107. wcscat (m_szLocation, m_szLocFolderRelativePath);
  2108. m_fDataValid = TRUE;
  2109. m_dwFlags = dwFlags;
  2110. m_bRemove = TRUE;
  2111. m_bValidGPO = FALSE; //since this is a removal.
  2112. m_szGPOName[0] = '\0';
  2113. DebugMsg((DM_VERBOSE, IDS_EFFECTIVE_REMOVE_POLICY, pGPO?pGPO->lpDisplayName:gSavedSettings[m_rID].m_szGPOName, m_szLocDisplayName));
  2114. CmpEffPolRem_End:
  2115. if (pwszReturnedString)
  2116. delete [] pwszReturnedString;
  2117. return Status;
  2118. }
  2119. CRedirectInfo::HasPolicy()
  2120. {
  2121. return ! ( m_dwFlags & REDIR_DONT_CARE );
  2122. }