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.

760 lines
18 KiB

  1. // Copyright (c) 1997-1999 Microsoft Corporation
  2. //
  3. // Post-operation shortcut (shell link) code
  4. //
  5. // 1 Dec 1999 sburns
  6. #include "headers.hxx"
  7. #include "ProgressDialog.hpp"
  8. #include "state.hpp"
  9. #include "resource.h"
  10. // @@ need to make sure that, when deleting shortcuts, we consider the case
  11. // were the shortcuts may have been added by the adminpak from the 5.0 release
  12. // of the product, not by ourselves in a later release.
  13. //
  14. // This case is: promote with version 5.0, upgrade to later version, demote
  15. struct ShortcutParams
  16. {
  17. int linkNameResId;
  18. int descResId;
  19. const wchar_t* target;
  20. const wchar_t* params;
  21. const wchar_t* iconDll;
  22. };
  23. // "Add" from the point of view of promotion: these are removed on demotion.
  24. static ShortcutParams shortcutsToAdd[] =
  25. {
  26. {
  27. // Active Directory Sites and Services
  28. IDS_DS_SITE_LINK,
  29. IDS_DS_SITE_DESC,
  30. L"dssite.msc",
  31. L"",
  32. // the .msc file contains the proper icon, so we don't need to
  33. // specify a dll from whence to retrieve an icon.
  34. L""
  35. },
  36. {
  37. // Active Directory Users and Computers
  38. IDS_DS_USERS_LINK,
  39. IDS_DS_USERS_DESC,
  40. L"dsa.msc",
  41. L"",
  42. L""
  43. },
  44. {
  45. // Active Directory Domains and Trusts
  46. IDS_DS_DOMAINS_LINK,
  47. IDS_DS_DOMAINS_DESC,
  48. L"domain.msc",
  49. L"",
  50. L""
  51. },
  52. {
  53. // Domain Controller Security Policy
  54. // if you change this name, be sure to change the code in
  55. // PromoteConfigureToolShortcuts too
  56. IDS_DC_POLICY_LINK,
  57. IDS_DC_POLICY_DESC,
  58. L"dcpol.msc",
  59. L"",
  60. L""
  61. },
  62. {
  63. // Domain Security Policy
  64. // if you change this name, be sure to change the code in
  65. // PromoteConfigureToolShortcuts too
  66. IDS_DOMAIN_POLICY_LINK,
  67. IDS_DOMAIN_POLICY_DESC,
  68. L"dompol.msc",
  69. L"",
  70. L""
  71. }
  72. };
  73. // "Delete" from the point of view of promotion: these are added back again
  74. // on demotion.
  75. static ShortcutParams shortcutsToDelete[] =
  76. {
  77. {
  78. // Local Security Policy
  79. IDS_LOCAL_POLICY_LINK,
  80. IDS_LOCAL_POLICY_DESC,
  81. L"secpol.msc",
  82. L"/s",
  83. L"wsecedit.dll"
  84. }
  85. };
  86. // Extracts the target of a shortcut: that to which the shortcut points.
  87. // Returns S_OK on success, and sets result to that target. On error, a COM
  88. // error code is returned and result is empty.
  89. //
  90. // shellLink - pointer to instance of object implementing IShellLink, which
  91. // has been associated with a shortcut file.
  92. //
  93. // result - receives the result -- the shortcut target path -- on sucess.
  94. HRESULT
  95. GetShortcutTargetPath(
  96. const SmartInterface<IShellLink>& shellLink,
  97. String& result)
  98. {
  99. LOG_FUNCTION(GetShortcutTargetPath);
  100. ASSERT(shellLink);
  101. result.erase();
  102. wchar_t target[MAX_PATH + 1];
  103. memset(&target, 0, sizeof(wchar_t) * (MAX_PATH + 1));
  104. HRESULT hr = shellLink->GetPath(target, MAX_PATH, 0, SLGP_SHORTPATH);
  105. if (SUCCEEDED(hr))
  106. {
  107. result = target;
  108. }
  109. return hr;
  110. }
  111. // Return true if the supplied target of a shortcut is such that it identifies
  112. // the shortcut as one of those installed on promote. Return false if not one
  113. // such.
  114. //
  115. // target - target path of the shortcut (i.e. path to that which the shortcut
  116. // points)
  117. bool
  118. IsAdminpakShortcut(const String& target)
  119. {
  120. LOG_FUNCTION2(IsAdminpakShortcut, target);
  121. // don't assert that target has a value. Some shortcuts don't, if they're
  122. // broken.
  123. //
  124. // ASSERT(!target.empty());
  125. // If the target is of the form %systemroot%\Installer\{guid}\foo.ico,
  126. // then it is one of the adminpak dcpromo shortcuts.
  127. static String baseNames[] =
  128. {
  129. L"DTMgmt.ico",
  130. L"ADSSMgr.ico",
  131. L"ADMgr.ico",
  132. L"ADDcPol.ico",
  133. L"ADDomPol.ico"
  134. };
  135. static String root(Win::GetSystemWindowsDirectory() + L"\\Installer\\{");
  136. bool result = false;
  137. String prefix(target, 0, root.length());
  138. if (root.icompare(prefix) == 0)
  139. {
  140. // the prefix matches.
  141. String leaf = FS::GetPathLeafElement(target);
  142. for (int i = 0; i < (sizeof(baseNames) / sizeof(String)) ; ++i)
  143. {
  144. if (leaf.icompare(baseNames[i]) == 0)
  145. {
  146. result = true;
  147. break;
  148. }
  149. }
  150. }
  151. LOG(
  152. String::format(
  153. L"%1 an adminpak shortcut",
  154. result ? L"is" : L"is not"));
  155. return result;
  156. }
  157. bool
  158. IsPromoteToolShortcut(const String& target)
  159. {
  160. LOG_FUNCTION2(IsPromoteToolShortcut, target);
  161. ASSERT(!target.empty());
  162. // Check target against the values we used to create the shortcuts. The
  163. // values we used specified a fully-qualified path to the system32 folder,
  164. // and we will compare the target to the full path.
  165. String targetPrefix = Win::GetSystemDirectory() + L"\\";
  166. for (
  167. int i = 0;
  168. i < sizeof(shortcutsToAdd) / sizeof(ShortcutParams);
  169. ++i)
  170. {
  171. if (target.icompare(targetPrefix + shortcutsToAdd[i].target) == 0)
  172. {
  173. return true;
  174. }
  175. }
  176. return false;
  177. }
  178. // Return true if the given shortcut one of those installed on promote.
  179. // Return false if not one of those shortcuts, or on error.
  180. //
  181. // shellLink - smart interface pointer to an object implementing IShellLink.
  182. //
  183. // lnkPath - full file path of the shortcut (.lnk) file to be evaluated.
  184. bool
  185. ShouldDeleteShortcut(
  186. const SmartInterface<IShellLink>& shellLink,
  187. const String& lnkPath)
  188. {
  189. LOG_FUNCTION2(ShouldDeleteShortcut, lnkPath);
  190. ASSERT(!lnkPath.empty());
  191. ASSERT(shellLink);
  192. // Shortcut file names are localized, so we can't delete them based on
  193. // their names. idea: Open the shortcut, see what it's target is,
  194. // and based on that, determine if it's one we should delete.
  195. HRESULT hr = S_OK;
  196. bool result = false;
  197. do
  198. {
  199. // Load the shortcut file
  200. SmartInterface<IPersistFile> ipf;
  201. hr = ipf.AcquireViaQueryInterface(shellLink);
  202. BREAK_ON_FAILED_HRESULT(hr);
  203. hr = ipf->Load(lnkPath.c_str(), STGM_READ);
  204. BREAK_ON_FAILED_HRESULT(hr);
  205. // Get the target lnkPath
  206. String target;
  207. hr = GetShortcutTargetPath(shellLink, target);
  208. BREAK_ON_FAILED_HRESULT(hr);
  209. if (IsAdminpakShortcut(target))
  210. {
  211. result = true;
  212. break;
  213. }
  214. // Not an adminpak shortcut. Might be one of the ones created by
  215. // PromoteConfigureToolShortcuts (ourselves).
  216. if (IsPromoteToolShortcut(target))
  217. {
  218. result = true;
  219. break;
  220. }
  221. // if we make it here, the shortcut is not one we should delete.
  222. }
  223. while (0);
  224. LOG(
  225. String::format(
  226. L"%1 delete shortcut",
  227. result ? L"should" : L"should not"));
  228. return result;
  229. }
  230. HRESULT
  231. CreateShortcut(
  232. const SmartInterface<IShellLink>& shellLink,
  233. const String& destFolderPath,
  234. int linkNameResId,
  235. int descResId,
  236. const String& target,
  237. const String& params,
  238. const String& iconDll)
  239. {
  240. LOG_FUNCTION2(CreateShortcut, target);
  241. ASSERT(shellLink);
  242. ASSERT(!destFolderPath.empty());
  243. ASSERT(!target.empty());
  244. ASSERT(linkNameResId);
  245. ASSERT(descResId);
  246. // params and iconDll may be empty
  247. HRESULT hr = S_OK;
  248. do
  249. {
  250. String sys32Folder = Win::GetSystemDirectory();
  251. String targetPath = sys32Folder + L"\\" + target;
  252. hr = shellLink->SetPath(targetPath.c_str());
  253. BREAK_ON_FAILED_HRESULT(hr);
  254. hr = shellLink->SetWorkingDirectory(sys32Folder.c_str());
  255. BREAK_ON_FAILED_HRESULT(hr);
  256. hr = shellLink->SetDescription(String::load(descResId).c_str());
  257. BREAK_ON_FAILED_HRESULT(hr);
  258. hr = shellLink->SetArguments(params.c_str());
  259. BREAK_ON_FAILED_HRESULT(hr);
  260. if (!iconDll.empty())
  261. {
  262. hr =
  263. shellLink->SetIconLocation(
  264. (sys32Folder + L"\\" + iconDll).c_str(), 0);
  265. }
  266. SmartInterface<IPersistFile> ipf;
  267. hr = ipf.AcquireViaQueryInterface(shellLink);
  268. BREAK_ON_FAILED_HRESULT(hr);
  269. String destPath =
  270. destFolderPath + L"\\" + String::load(linkNameResId) + L".lnk";
  271. hr = ipf->Save(destPath.c_str(), TRUE);
  272. BREAK_ON_FAILED_HRESULT(hr);
  273. }
  274. while (0);
  275. LOG_HRESULT(hr);
  276. return hr;
  277. }
  278. HRESULT
  279. DeleteShortcut(
  280. const String& folder,
  281. int linkNameResId)
  282. {
  283. LOG_FUNCTION(DeleteShortcut);
  284. ASSERT(!folder.empty());
  285. ASSERT(linkNameResId);
  286. HRESULT hr = S_OK;
  287. do
  288. {
  289. String linkPath =
  290. folder + L"\\" + String::load(linkNameResId) + L".lnk";
  291. LOG(linkPath);
  292. if (FS::PathExists(linkPath))
  293. {
  294. hr = Win::DeleteFile(linkPath);
  295. BREAK_ON_FAILED_HRESULT(hr);
  296. }
  297. }
  298. while (0);
  299. return hr;
  300. }
  301. // Remove the shortcuts to the DS administration tools that were installed on
  302. // promote.
  303. void
  304. DemoteConfigureToolShortcuts(ProgressDialog& dialog)
  305. {
  306. LOG_FUNCTION(DemoteConfigureToolShortcuts);
  307. HRESULT hr = S_OK;
  308. State& state = State::GetInstance();
  309. do
  310. {
  311. String path = state.GetAdminToolsShortcutPath();
  312. if (path.empty())
  313. {
  314. // We were unable to determine the path at startup.
  315. hr = Win32ToHresult(ERROR_PATH_NOT_FOUND);
  316. break;
  317. }
  318. // (may) Need to init com for this thread.
  319. hr = ::CoInitialize(0);
  320. BREAK_ON_FAILED_HRESULT(hr);
  321. SmartInterface<IShellLink> shellLink;
  322. hr =
  323. shellLink.AcquireViaCreateInstance(
  324. CLSID_ShellLink,
  325. 0,
  326. CLSCTX_INPROC_SERVER);
  327. BREAK_ON_FAILED_HRESULT(hr);
  328. LOG(L"enumerating shortcuts");
  329. FS::Iterator iter(
  330. path + L"\\*.lnk",
  331. FS::Iterator::INCLUDE_FILES
  332. | FS::Iterator::RETURN_FULL_PATHS);
  333. String current;
  334. while ((hr = iter.GetCurrent(current)) == S_OK)
  335. {
  336. if (ShouldDeleteShortcut(shellLink, current))
  337. {
  338. LOG(String::format(L"Deleting %1", current.c_str()));
  339. // we don't bail out on an error here because we want to
  340. // try to delete as many shortcuts as possible.
  341. HRESULT unused = Win::DeleteFile(current);
  342. LOG_HRESULT(unused);
  343. }
  344. hr = iter.Increment();
  345. BREAK_ON_FAILED_HRESULT(hr);
  346. }
  347. // add the shortcut(s) removed during promote
  348. for (
  349. int i = 0;
  350. i < sizeof(shortcutsToDelete) / sizeof(ShortcutParams);
  351. ++i)
  352. {
  353. // don't break on error -- push on to attempt to create the
  354. // entire set.
  355. CreateShortcut(
  356. shellLink,
  357. path,
  358. shortcutsToDelete[i].linkNameResId,
  359. shortcutsToDelete[i].descResId,
  360. shortcutsToDelete[i].target,
  361. shortcutsToDelete[i].params,
  362. shortcutsToDelete[i].iconDll);
  363. }
  364. }
  365. while (0);
  366. if (FAILED(hr))
  367. {
  368. popup.Error(
  369. dialog.GetHWND(),
  370. hr,
  371. IDS_ERROR_CONFIGURING_SHORTCUTS);
  372. state.AddFinishMessage(
  373. String::load(IDS_SHORTCUTS_NOT_CONFIGURED));
  374. }
  375. }
  376. // Take a domain name in canonical (dotted) form, e.g. domain.foo.com, and
  377. // translate it to the fully-qualified DN form, e.g. DC=domain,DC=foo,DC=com
  378. //
  379. // domainCanonical - in, domain name in canonical form
  380. //
  381. // domainDN - out, domain name in DN form
  382. HRESULT
  383. CannonicalToDn(const String& domainCanonical, String& domainDN)
  384. {
  385. LOG_FUNCTION2(CannonicalToDn, domainCanonical);
  386. ASSERT(!domainCanonical.empty());
  387. domainDN.erase();
  388. HRESULT hr = S_OK;
  389. do
  390. {
  391. if (domainCanonical.empty())
  392. {
  393. hr = E_INVALIDARG;
  394. BREAK_ON_FAILED_HRESULT(hr);
  395. }
  396. // add a trailing '/' to signal DsCrackNames to do a syntactical
  397. // munge of the string, rather than hit the wire.
  398. // add 1 for the null terminator, 1 for the trailing '/'
  399. PWSTR name = new WCHAR[domainCanonical.length() + 2];
  400. memset(name, 0, (domainCanonical.length() + 2) * sizeof(WCHAR));
  401. domainCanonical.copy(name, domainCanonical.length());
  402. name[domainCanonical.length()] = L'/';
  403. DS_NAME_RESULT* nameResult = 0;
  404. hr =
  405. Win32ToHresult(
  406. ::DsCrackNames(
  407. // no handle: this is a string munge
  408. reinterpret_cast<void*>(-1),
  409. DS_NAME_FLAG_SYNTACTICAL_ONLY,
  410. DS_CANONICAL_NAME,
  411. DS_FQDN_1779_NAME,
  412. 1,
  413. &name,
  414. &nameResult));
  415. delete[] name;
  416. BREAK_ON_FAILED_HRESULT(hr);
  417. ASSERT(nameResult);
  418. if (nameResult)
  419. {
  420. ASSERT(nameResult->cItems == 1);
  421. DS_NAME_RESULT_ITEM* items = nameResult->rItems;
  422. LOG(String::format(L"status : 0x%1!X!", items[0].status));
  423. LOG(String::format(L"pDomain: %1", items[0].pDomain));
  424. LOG(String::format(L"pName : %1", items[0].pName));
  425. ASSERT(items[0].status == DS_NAME_NO_ERROR);
  426. if (items[0].pName)
  427. {
  428. domainDN = items[0].pName;
  429. }
  430. if (domainDN.empty())
  431. {
  432. hr = E_FAIL;
  433. }
  434. ::DsFreeNameResult(nameResult);
  435. }
  436. }
  437. while (0);
  438. LOG_HRESULT(hr);
  439. return hr;
  440. }
  441. // Create all the admin tools shortcuts that are needed after a promote.
  442. //
  443. // path - in, where to create the shortcuts
  444. //
  445. // shellLink - in, initialized shellLink interface to create the shortcuts
  446. // with.
  447. HRESULT
  448. PromoteCreateShortcuts(const String& path, SmartInterface<IShellLink>& shellLink)
  449. {
  450. LOG_FUNCTION(PromoteCreateShortcuts);
  451. ASSERT(!path.empty());
  452. ASSERT(shellLink);
  453. HRESULT hr = S_OK;
  454. do
  455. {
  456. State& state = State::GetInstance();
  457. // for the policy shortcuts, we will need to know the domain DN, so
  458. // determine that here.
  459. // NTRAID#NTBUG9-232442-2000/11/15-sburns
  460. String domainCanonical;
  461. State::Operation oper = state.GetOperation();
  462. if (
  463. oper == State::FOREST
  464. || oper == State::TREE
  465. || oper == State::CHILD)
  466. {
  467. domainCanonical = state.GetNewDomainDNSName();
  468. }
  469. else if (oper == State::REPLICA)
  470. {
  471. domainCanonical = state.GetReplicaDomainDNSName();
  472. }
  473. else
  474. {
  475. // we should not be calling this function on non-promote scenarios
  476. ASSERT(false);
  477. hr = E_FAIL;
  478. BREAK_ON_FAILED_HRESULT(hr);
  479. }
  480. String domainDn;
  481. bool skipPolicyShortcuts = false;
  482. hr = CannonicalToDn(domainCanonical, domainDn);
  483. if (FAILED(hr))
  484. {
  485. LOG(L"skipping install of policy shortcuts");
  486. skipPolicyShortcuts = true;
  487. }
  488. for (
  489. int i = 0;
  490. i < sizeof(shortcutsToAdd) / sizeof(ShortcutParams);
  491. ++i)
  492. {
  493. // set the correct parameters for domain and dc security policy tools.
  494. String params;
  495. if (shortcutsToAdd[i].linkNameResId == IDS_DC_POLICY_LINK)
  496. {
  497. if (skipPolicyShortcuts)
  498. {
  499. continue;
  500. }
  501. params =
  502. String::format(
  503. L"/gpobject:\"LDAP://CN={%1},CN=Policies,CN=System,%2\"",
  504. STR_DEFAULT_DOMAIN_CONTROLLER_GPO_GUID,
  505. domainDn.c_str());
  506. }
  507. else if (shortcutsToAdd[i].linkNameResId == IDS_DOMAIN_POLICY_LINK)
  508. {
  509. if (skipPolicyShortcuts)
  510. {
  511. continue;
  512. }
  513. params =
  514. String::format(
  515. L"/gpobject:\"LDAP://CN={%1},CN=Policies,CN=System,%2\"",
  516. STR_DEFAULT_DOMAIN_GPO_GUID,
  517. domainDn.c_str());
  518. }
  519. else
  520. {
  521. params = shortcutsToAdd[i].params;
  522. }
  523. // don't break on errors -- push on to attempt to create the
  524. // entire set.
  525. CreateShortcut(
  526. shellLink,
  527. path,
  528. shortcutsToAdd[i].linkNameResId,
  529. shortcutsToAdd[i].descResId,
  530. shortcutsToAdd[i].target,
  531. params,
  532. shortcutsToAdd[i].iconDll);
  533. }
  534. }
  535. while (0);
  536. LOG_HRESULT(hr);
  537. return hr;
  538. }
  539. void
  540. PromoteConfigureToolShortcuts(ProgressDialog& dialog)
  541. {
  542. LOG_FUNCTION(PromoteConfigureToolShortcuts);
  543. dialog.UpdateText(String::load(IDS_CONFIGURING_SHORTCUTS));
  544. HRESULT hr = S_OK;
  545. State& state = State::GetInstance();
  546. do
  547. {
  548. String path = state.GetAdminToolsShortcutPath();
  549. if (path.empty())
  550. {
  551. // We were unable to determine the path at startup.
  552. hr = Win32ToHresult(ERROR_PATH_NOT_FOUND);
  553. break;
  554. }
  555. // Need to init com for this thread.
  556. hr = ::CoInitialize(0);
  557. BREAK_ON_FAILED_HRESULT(hr);
  558. SmartInterface<IShellLink> shellLink;
  559. hr =
  560. shellLink.AcquireViaCreateInstance(
  561. CLSID_ShellLink,
  562. 0,
  563. CLSCTX_INPROC_SERVER);
  564. BREAK_ON_FAILED_HRESULT(hr);
  565. // add the shortcuts to the ds administration tools
  566. PromoteCreateShortcuts(path, shellLink);
  567. // remove the shortcuts to local tools
  568. for (
  569. int i = 0;
  570. i < sizeof(shortcutsToDelete) / sizeof(ShortcutParams);
  571. ++i)
  572. {
  573. // don't break on error -- push on to attempt to delete the
  574. // entire set.
  575. DeleteShortcut(
  576. path,
  577. shortcutsToDelete[i].linkNameResId);
  578. }
  579. }
  580. while (0);
  581. if (FAILED(hr))
  582. {
  583. popup.Error(
  584. dialog.GetHWND(),
  585. hr,
  586. IDS_ERROR_CONFIGURING_SHORTCUTS);
  587. state.AddFinishMessage(
  588. String::load(IDS_SHORTCUTS_NOT_CONFIGURED));
  589. }
  590. }