Leaked source code of windows server 2003
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.

3192 lines
117 KiB

  1. //+----------------------------------------------------------------------------
  2. //
  3. // File: install.cpp
  4. //
  5. // Module: CMSTP.EXE
  6. //
  7. // Synopsis: This source file contains the code for installing CM profiles.
  8. //
  9. // Copyright (c) 1997-1999 Microsoft Corporation
  10. //
  11. // Author: quintinb Created 07/14/98
  12. //
  13. //+----------------------------------------------------------------------------
  14. #include "cmmaster.h"
  15. #include "installerfuncs.h"
  16. #include "winuserp.h"
  17. #include <aclapi.h>
  18. #include <advpub.h>
  19. #include "tunl_str.h"
  20. #include "cmsecure.h"
  21. #include "gppswithalloc.cpp"
  22. //
  23. // This global var, contains the path to the source files to install such as the
  24. // cmp, cms, and inf. (From the inf path passed in to InstallInf).
  25. //
  26. TCHAR g_szProfileSourceDir[MAX_PATH+1];
  27. // This is really ugly, we need to consolidate our platform detection code between CM and
  28. // the setup components.
  29. BOOL IsNT()
  30. {
  31. CPlatform plat;
  32. return plat.IsNT();
  33. }
  34. #define OS_NT (IsNT())
  35. BOOL IsAtLeastNT5()
  36. {
  37. CPlatform plat;
  38. return plat.IsAtLeastNT5();
  39. }
  40. #define OS_NT5 (IsAtLeastNT5())
  41. #include "MemberOfGroup.cpp"
  42. #include "cmexitwin.cpp"
  43. //+----------------------------------------------------------------------------
  44. //
  45. // Function: SetPermissionsOnWin2kExceptionUninstallRegKeys
  46. //
  47. // Synopsis: This function sets the permissions on the uninstall registry keys
  48. // created by the Windows 2000 exception installer. The function
  49. // gives Everyone read access, Local Administrators full control, and
  50. // the CREATOR OWNER full control.
  51. //
  52. // Arguments: None
  53. //
  54. // Returns: HRESULT -- standard COM style error codes
  55. //
  56. // History: quintinb Created 10/04/01
  57. //
  58. //+----------------------------------------------------------------------------
  59. HRESULT SetPermissionsOnWin2kExceptionUninstallRegKeys()
  60. {
  61. DWORD dwRes;
  62. HRESULT hr = S_OK;
  63. LPWSTR pszCmExceptionUninstallKey = L"MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\CMEXCEPT";
  64. PACL pNewAccessList = NULL;
  65. PSID pEveryoneSid = NULL;
  66. PSID pAdministratorsSid = NULL;
  67. PSID pCreatorSid = NULL;
  68. PSID pCurrentUserSid = NULL;
  69. SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
  70. SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
  71. SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
  72. EXPLICIT_ACCESS_W AccessEntryArray[3] = {0};
  73. PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL;
  74. HANDLE pTokenHandle = NULL;
  75. PTOKEN_USER ptu = NULL;
  76. DWORD dwNeeded = 0;
  77. BOOL bRet;
  78. //
  79. // Link to Advapi32.dll so that we can load the security functions we need from it
  80. //
  81. typedef BOOL (WINAPI *pfnAllocateAndInitializeSidSpec)(PSID_IDENTIFIER_AUTHORITY, BYTE, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, PSID);
  82. typedef DWORD (WINAPI* pfnSetEntriesInAclWSpec)(ULONG, PEXPLICIT_ACCESS_W, PACL, PACL *);
  83. typedef DWORD (WINAPI* pfnSetNamedSecurityInfoWSpec)(LPWSTR, SE_OBJECT_TYPE, SECURITY_INFORMATION, PSID, PSID, PACL, PACL);
  84. typedef PVOID (WINAPI* pfnFreeSidSpec)(PSID);
  85. typedef VOID (WINAPI* pfnBuildTrusteeWithSidWSpec)(PTRUSTEE_W, PSID);
  86. typedef BOOL (WINAPI* pfnOpenProcessTokenSpec)(HANDLE, DWORD, PHANDLE);
  87. typedef BOOL (WINAPI* pfnGetTokenInformationSpec)(HANDLE, TOKEN_INFORMATION_CLASS, LPVOID, DWORD, PDWORD);
  88. pfnAllocateAndInitializeSidSpec pfnAllocateAndInitializeSid = NULL;
  89. pfnSetEntriesInAclWSpec pfnSetEntriesInAclW = NULL;
  90. pfnSetNamedSecurityInfoWSpec pfnSetNamedSecurityInfoW = NULL;
  91. pfnFreeSidSpec pfnFreeSid = NULL;
  92. pfnBuildTrusteeWithSidWSpec pfnBuildTrusteeWithSidW = NULL;
  93. pfnOpenProcessTokenSpec pfnOpenProcessToken = NULL;
  94. pfnGetTokenInformationSpec pfnGetTokenInformation = NULL;
  95. HMODULE hAdvapi32 = LoadLibrary(TEXT("advapi32.dll"));
  96. if (hAdvapi32)
  97. {
  98. pfnAllocateAndInitializeSid = (pfnAllocateAndInitializeSidSpec)GetProcAddress(hAdvapi32, "AllocateAndInitializeSid");
  99. pfnSetEntriesInAclW = (pfnSetEntriesInAclWSpec)GetProcAddress(hAdvapi32, "SetEntriesInAclW");
  100. pfnSetNamedSecurityInfoW = (pfnSetNamedSecurityInfoWSpec)GetProcAddress(hAdvapi32, "SetNamedSecurityInfoW");
  101. pfnFreeSid = (pfnFreeSidSpec)GetProcAddress(hAdvapi32, "FreeSid");
  102. pfnBuildTrusteeWithSidW = (pfnBuildTrusteeWithSidWSpec)GetProcAddress(hAdvapi32, "BuildTrusteeWithSidW");
  103. pfnOpenProcessToken = (pfnOpenProcessTokenSpec)GetProcAddress(hAdvapi32, "OpenProcessToken");
  104. pfnGetTokenInformation = (pfnGetTokenInformationSpec)GetProcAddress(hAdvapi32, "GetTokenInformation");
  105. if (0 == (pfnOpenProcessToken && pfnGetTokenInformation &&
  106. pfnAllocateAndInitializeSid && pfnSetEntriesInAclW &&
  107. pfnSetNamedSecurityInfoW && pfnFreeSid &&
  108. pfnBuildTrusteeWithSidW))
  109. {
  110. hr = HRESULT_FROM_WIN32(ERROR_PROC_NOT_FOUND);
  111. goto exit;
  112. }
  113. }
  114. else
  115. {
  116. hr = HRESULT_FROM_WIN32(GetLastError());
  117. goto exit;
  118. }
  119. //
  120. // Get the SID for Everyone
  121. //
  122. bRet = pfnAllocateAndInitializeSid (&WorldSidAuthority, 1, SECURITY_WORLD_RID,
  123. 0, 0, 0, 0, 0, 0, 0, &pEveryoneSid);
  124. if (!bRet || (NULL == pEveryoneSid))
  125. {
  126. hr = HRESULT_FROM_WIN32(GetLastError());
  127. goto exit;
  128. }
  129. //
  130. // Get the SID for the Local Administrators group
  131. //
  132. bRet = pfnAllocateAndInitializeSid (&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
  133. 0, 0, 0, 0, 0, 0, &pAdministratorsSid);
  134. if (!bRet || (NULL == pAdministratorsSid))
  135. {
  136. hr = HRESULT_FROM_WIN32(GetLastError());
  137. goto exit;
  138. }
  139. //
  140. // Get the SID the for CREATOR OWNER
  141. //
  142. bRet = pfnAllocateAndInitializeSid (&CreatorSidAuthority, 1, SECURITY_CREATOR_OWNER_RID,
  143. 0, 0, 0, 0, 0, 0, 0, &pCreatorSid);
  144. if (!bRet || (NULL == pCreatorSid))
  145. {
  146. hr = HRESULT_FROM_WIN32(GetLastError());
  147. goto exit;
  148. }
  149. bRet = pfnOpenProcessToken(GetCurrentProcess(), TOKEN_READ , &pTokenHandle);
  150. if (bRet && pTokenHandle)
  151. {
  152. bRet = pfnGetTokenInformation(pTokenHandle, TokenUser, NULL, 0, &dwNeeded);
  153. if (dwNeeded)
  154. {
  155. ptu = (PTOKEN_USER)CmMalloc(dwNeeded);
  156. if (ptu)
  157. {
  158. DWORD dwNeededAgain = 0;
  159. bRet = pfnGetTokenInformation(pTokenHandle, TokenUser, ptu, dwNeeded, &dwNeededAgain);
  160. if (bRet)
  161. {
  162. pCurrentUserSid = ptu->User.Sid;
  163. }
  164. }
  165. }
  166. CloseHandle(pTokenHandle);
  167. pTokenHandle = NULL;
  168. }
  169. if (!bRet || (NULL == pCurrentUserSid))
  170. {
  171. hr = HRESULT_FROM_WIN32(GetLastError());
  172. goto exit;
  173. }
  174. //
  175. // Give Everyone generic read access
  176. //
  177. pfnBuildTrusteeWithSidW(&(AccessEntryArray[0].Trustee), pEveryoneSid);
  178. AccessEntryArray[0].grfInheritance = NO_INHERITANCE;
  179. AccessEntryArray[0].grfAccessMode = GRANT_ACCESS;
  180. AccessEntryArray[0].grfAccessPermissions = GENERIC_READ;
  181. //
  182. // Give the local Administrators group full control
  183. //
  184. pfnBuildTrusteeWithSidW(&(AccessEntryArray[1].Trustee), pAdministratorsSid);
  185. AccessEntryArray[1].grfInheritance = NO_INHERITANCE;
  186. AccessEntryArray[1].grfAccessMode = GRANT_ACCESS;
  187. AccessEntryArray[1].grfAccessPermissions = (STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL);
  188. //
  189. // Give current user full control
  190. //
  191. pfnBuildTrusteeWithSidW(&(AccessEntryArray[2].Trustee), pCurrentUserSid);
  192. AccessEntryArray[2].grfInheritance = NO_INHERITANCE;
  193. AccessEntryArray[2].grfAccessMode = GRANT_ACCESS;
  194. AccessEntryArray[2].grfAccessPermissions = (STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL);
  195. //
  196. // Build an access list from the access list entry.
  197. //
  198. dwRes = pfnSetEntriesInAclW(3, &(AccessEntryArray[0]), NULL, &pNewAccessList);
  199. if (ERROR_SUCCESS == dwRes)
  200. {
  201. //
  202. // Set the access-control information in the object's DACL. Note that setting PROTECTED_DACL_SECURITY_INFORMATION
  203. // turns off the inherited permissions.
  204. //
  205. dwRes = pfnSetNamedSecurityInfoW(pszCmExceptionUninstallKey, // name of the object
  206. SE_REGISTRY_KEY, // type of object
  207. (DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION), // type of information to set
  208. NULL, // pointer to the new owner SID
  209. NULL, // pointer to the new primary group SID
  210. pNewAccessList, // pointer to new DACL
  211. NULL); // pointer to new SACL
  212. }
  213. hr = HRESULT_FROM_WIN32(dwRes);
  214. exit:
  215. if (ptu)
  216. {
  217. CmFree(ptu);
  218. ptu = NULL;
  219. }
  220. if (pEveryoneSid && pfnFreeSid)
  221. {
  222. pfnFreeSid(pEveryoneSid);
  223. }
  224. if (pAdministratorsSid && pfnFreeSid)
  225. {
  226. pfnFreeSid(pAdministratorsSid);
  227. }
  228. if (pCreatorSid && pfnFreeSid)
  229. {
  230. pfnFreeSid(pCreatorSid);
  231. }
  232. if (pNewAccessList)
  233. {
  234. LocalFree(pNewAccessList);
  235. }
  236. if (pSecurityDescriptor)
  237. {
  238. LocalFree(pSecurityDescriptor);
  239. }
  240. if (hAdvapi32)
  241. {
  242. FreeLibrary(hAdvapi32);
  243. }
  244. return hr;
  245. }
  246. //+----------------------------------------------------------------------------
  247. //
  248. // Function: CheckIeDllRequirements
  249. //
  250. // Synopsis: This function checks to see if the browser agnostic dlls are of
  251. // a sufficient version for CM to work, or if we should copy the
  252. // dlls we carry in the package with us.
  253. //
  254. // Arguments: CPlatform* pPlat - a CPlatform object
  255. //
  256. // Returns: BOOL - returns TRUE if all browser files meet the requirements, FALSE
  257. // if any one of the files fails to meet what CM needs.
  258. //
  259. // History: quintinb Created Header 5/24/99
  260. //
  261. //+----------------------------------------------------------------------------
  262. BOOL CheckIeDllRequirements(CPlatform* pPlat)
  263. {
  264. TCHAR szSysDir[MAX_PATH+1];
  265. TCHAR szDllToCheck[MAX_PATH+1];
  266. if(GetSystemDirectory(szSysDir, MAX_PATH))
  267. {
  268. if (pPlat->IsWin9x())
  269. {
  270. //
  271. // Need Advapi32.dll to be version 4.70.0.1215 or greater.
  272. //
  273. const DWORD c_dwRequiredAdvapi32Version = (4 << c_iShiftAmount) + 70;
  274. const DWORD c_dwRequiredAdvapi32BuildNumber = 1215;
  275. MYVERIFY(CELEMS(szDllToCheck) > (UINT)wsprintf(szDllToCheck, TEXT("%s%s"),
  276. szSysDir, TEXT("\\advapi32.dll")));
  277. CVersion AdvApi32Version(szDllToCheck);
  278. if ((c_dwRequiredAdvapi32Version > AdvApi32Version.GetVersionNumber()) ||
  279. ((c_dwRequiredAdvapi32Version == AdvApi32Version.GetVersionNumber()) &&
  280. (c_dwRequiredAdvapi32BuildNumber > AdvApi32Version.GetBuildAndQfeNumber())))
  281. {
  282. return FALSE;
  283. }
  284. //
  285. // Need comctl32.dll to be version 4.70.0.1146 or greater.
  286. //
  287. const DWORD c_dwRequiredComctl32Version = (4 << c_iShiftAmount) + 70;
  288. const DWORD c_dwRequiredComctl32BuildNumber = 1146;
  289. MYVERIFY(CELEMS(szDllToCheck) > (UINT)wsprintf(szDllToCheck, TEXT("%s%s"),
  290. szSysDir, TEXT("\\comctl32.dll")));
  291. CVersion Comctl32Version(szDllToCheck);
  292. if ((c_dwRequiredComctl32Version > Comctl32Version.GetVersionNumber()) ||
  293. ((c_dwRequiredComctl32Version == Comctl32Version.GetVersionNumber()) &&
  294. (c_dwRequiredComctl32BuildNumber > Comctl32Version.GetBuildAndQfeNumber())))
  295. {
  296. return FALSE;
  297. }
  298. //
  299. // Need rnaph.dll to be version 4.40.311.0 or greater.
  300. //
  301. const DWORD c_dwRequiredRnaphVersion = (4 << c_iShiftAmount) + 40;
  302. const DWORD c_dwRequiredRnaphBuildNumber = (311 << c_iShiftAmount);
  303. MYVERIFY(CELEMS(szDllToCheck) > (UINT)wsprintf(szDllToCheck, TEXT("%s%s"),
  304. szSysDir, TEXT("\\rnaph.dll")));
  305. CVersion RnaphVersion(szDllToCheck);
  306. if ((c_dwRequiredRnaphVersion > RnaphVersion.GetVersionNumber()) ||
  307. ((c_dwRequiredRnaphVersion == RnaphVersion.GetVersionNumber()) &&
  308. (c_dwRequiredRnaphBuildNumber > RnaphVersion.GetBuildAndQfeNumber())))
  309. {
  310. return FALSE;
  311. }
  312. }
  313. //
  314. // Need wininet.dll to be version 4.70.0.1301 or greater.
  315. //
  316. const DWORD c_dwRequiredWininetVersion = (4 << c_iShiftAmount) + 70;
  317. const DWORD c_dwRequiredWininetBuildNumber = 1301;
  318. MYVERIFY(CELEMS(szDllToCheck) > (UINT)wsprintf(szDllToCheck, TEXT("%s%s"),
  319. szSysDir, TEXT("\\wininet.dll")));
  320. CVersion WininetVersion(szDllToCheck);
  321. if ((c_dwRequiredWininetVersion > WininetVersion.GetVersionNumber()) ||
  322. ((c_dwRequiredWininetVersion == WininetVersion.GetVersionNumber()) &&
  323. (c_dwRequiredWininetBuildNumber > WininetVersion.GetBuildAndQfeNumber())))
  324. {
  325. return FALSE;
  326. }
  327. }
  328. else
  329. {
  330. return FALSE;
  331. }
  332. return TRUE;
  333. }
  334. //+----------------------------------------------------------------------------
  335. //
  336. // Function: WriteSingleUserProfileMappings
  337. //
  338. // Synopsis: This function write the single user mappings key.
  339. //
  340. // Arguments: HINSTANCE hInstance - an Instance handle to load string resources with
  341. // LPCTSTR pszShortServiceName - short service name of the profile
  342. // LPCTSTR pszServiceName - Long service name of the profile
  343. //
  344. // Returns: BOOL - TRUE if successful
  345. //
  346. // History: quintinb Created 5/23/99
  347. //
  348. //+----------------------------------------------------------------------------
  349. BOOL WriteSingleUserProfileMappings(LPCTSTR pszInstallDir, LPCTSTR pszShortServiceName, LPCTSTR pszServiceName)
  350. {
  351. BOOL bReturn = FALSE;
  352. TCHAR szCmpFile [MAX_PATH+1];
  353. TCHAR szTemp [MAX_PATH+1];
  354. TCHAR szUserProfilePath [MAX_PATH+1];
  355. HKEY hKey = NULL;
  356. //
  357. // Construct the Cmp Path
  358. //
  359. MYVERIFY(CELEMS(szCmpFile) > (UINT)wsprintf(szCmpFile, TEXT("%s\\%s.cmp"),
  360. pszInstallDir, pszShortServiceName));
  361. //
  362. // Figure out the User Profile directory
  363. //
  364. DWORD dwChars = ExpandEnvironmentStrings(TEXT("%AppData%"), szUserProfilePath, MAX_PATH);
  365. if (dwChars && (MAX_PATH >= dwChars))
  366. {
  367. //
  368. // We want to do a lstrcmpi but with only so many chars. Unfortunately this doesn't
  369. // exist in Win32 so we will use lstrcpyn into a temp buffer and then use lstrcmpi.
  370. //
  371. lstrcpyn(szTemp, szCmpFile, lstrlen(szUserProfilePath) + 1);
  372. if (0 == lstrcmpi(szTemp, szUserProfilePath))
  373. {
  374. lstrcpy(szTemp, szCmpFile + lstrlen(szUserProfilePath));
  375. lstrcpy(szCmpFile, TEXT("%AppData%"));
  376. lstrcat(szCmpFile, szTemp);
  377. }
  378. else
  379. {
  380. CMASSERTMSG(FALSE, TEXT("Unable to build the Single User Mappings key value, exiting."));
  381. goto exit;
  382. }
  383. //
  384. // Okay, now we need to write out the single user mappings key
  385. //
  386. DWORD dwDisposition;
  387. LONG lResult = RegCreateKeyEx(HKEY_CURRENT_USER, c_pszRegCmMappings, 0, NULL,
  388. REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL,
  389. &hKey, &dwDisposition);
  390. if (ERROR_SUCCESS == lResult)
  391. {
  392. DWORD dwType = REG_SZ;
  393. DWORD dwSize = lstrlen(szCmpFile) + 1;
  394. if (ERROR_SUCCESS != RegSetValueEx(hKey, pszServiceName, NULL, dwType,
  395. (CONST BYTE *)szCmpFile, dwSize))
  396. {
  397. CMASSERTMSG(FALSE, TEXT("Unable to write the Single User Mappings key value, exiting."));
  398. goto exit;
  399. }
  400. else
  401. {
  402. bReturn = TRUE;
  403. }
  404. }
  405. }
  406. else
  407. {
  408. CMASSERTMSG(FALSE, TEXT("Unable to expand the AppData String, exiting."));
  409. goto exit;
  410. }
  411. exit:
  412. if (hKey)
  413. {
  414. MYVERIFY(ERROR_SUCCESS == RegCloseKey(hKey));
  415. }
  416. return bReturn;
  417. }
  418. //+----------------------------------------------------------------------------
  419. //
  420. // Function: ProcessPreferencesUI
  421. //
  422. // Synopsis: This function processes messages for either of the two dialogs used
  423. // to ask the user if they want a desktop shortcut. One dialog is for
  424. // non-admins and only contains the shortcut question, the other dialog
  425. // is for local admins and also contains whether the admin wants the
  426. // profile installed for all users or just for single users.
  427. //
  428. //
  429. // History: quintinb Created 2/19/98
  430. // quintinb Renamed from ProcessAdminUI to ProcessPreferencesUI and
  431. // added new functionality 6/9/8
  432. // quintinb removed mention of Start Menu Shortcut 2/17/99
  433. //
  434. //+----------------------------------------------------------------------------
  435. BOOL APIENTRY ProcessPreferencesUI(
  436. HWND hDlg,
  437. UINT message,
  438. WPARAM wParam,
  439. LPARAM lParam)
  440. {
  441. int iUiChoices;
  442. HKEY hKey;
  443. DWORD dwSize;
  444. DWORD dwTemp;
  445. DWORD dwType;
  446. InitDialogStruct* pDialogArgs = NULL;
  447. switch (message)
  448. {
  449. case WM_INITDIALOG:
  450. //
  451. // Look up the preferences for Desktop Shortcuts/Start Menu Links
  452. // in the registry and set them accordingly.
  453. //
  454. pDialogArgs = (InitDialogStruct*)lParam;
  455. if (pDialogArgs->bNoDesktopIcon)
  456. {
  457. MYVERIFY(0 != CheckDlgButton(hDlg, IDC_DESKTOP, FALSE));
  458. }
  459. else
  460. {
  461. if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_CURRENT_USER, c_pszRegStickyUiDefault,
  462. 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hKey, &dwTemp))
  463. {
  464. //
  465. // The default of whether a desktop shortcut should be created is stored in the
  466. // registry. Get this value to populate the UI. (default is off)
  467. //
  468. dwType = REG_DWORD;
  469. dwSize = sizeof(DWORD);
  470. dwTemp = 0;
  471. RegQueryValueEx(hKey, c_pszRegDesktopShortCut, NULL, &dwType, (LPBYTE)&dwTemp,
  472. &dwSize); //lint !e534
  473. MYVERIFY(0 != CheckDlgButton(hDlg, IDC_DESKTOP, dwTemp));
  474. MYVERIFY(ERROR_SUCCESS == RegCloseKey(hKey));
  475. }
  476. }
  477. //
  478. // Set the Window Text to the Profile Name
  479. //
  480. MYVERIFY(FALSE != SetWindowText(hDlg, pDialogArgs->pszTitle));
  481. if (!(pDialogArgs->bSingleUser))
  482. {
  483. CheckDlgButton(hDlg, IDC_ALLUSERS, TRUE); //lint !e534 this will fail if using the nochoice UI
  484. //
  485. // Set focus to the All user radio button
  486. //
  487. HWND hControl = GetDlgItem(hDlg, IDC_ALLUSERS);
  488. if (hControl)
  489. {
  490. SetFocus(hControl);
  491. }
  492. }
  493. else
  494. {
  495. CheckDlgButton(hDlg, IDC_YOURSELF, TRUE); //lint !e534 this will fail if using the nochoice UI
  496. //
  497. // Set focus to the Single user radio button
  498. //
  499. HWND hControl = GetDlgItem(hDlg, IDC_YOURSELF);
  500. if (hControl)
  501. {
  502. SetFocus(hControl);
  503. }
  504. }
  505. //
  506. // We return FALSE here but the focus is correctly set.
  507. //
  508. break;
  509. case WM_COMMAND:
  510. switch (LOWORD(wParam))
  511. {
  512. case IDOK:
  513. //
  514. // Build the return value
  515. //
  516. if (IsDlgButtonChecked(hDlg, IDC_ALLUSERS) == BST_CHECKED)
  517. {
  518. iUiChoices = ALLUSERS;
  519. }
  520. else
  521. {
  522. iUiChoices = 0;
  523. }
  524. if (IsDlgButtonChecked(hDlg, IDC_DESKTOP))
  525. {
  526. iUiChoices |= CREATEDESKTOPICON;
  527. }
  528. //
  529. // Make sure to save the users preferences for Desktop Icons
  530. // and Start Menu Links.
  531. //
  532. if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_CURRENT_USER, c_pszRegStickyUiDefault,
  533. 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, &dwTemp))
  534. {
  535. //
  536. // Store the current state of whether we should create a desktop shortcut
  537. //
  538. dwTemp = IsDlgButtonChecked(hDlg, IDC_DESKTOP);
  539. MYVERIFY(ERROR_SUCCESS == RegSetValueEx(hKey, c_pszRegDesktopShortCut, 0,
  540. REG_DWORD, (LPBYTE)&dwTemp, sizeof(DWORD)));
  541. MYVERIFY(ERROR_SUCCESS == RegCloseKey(hKey));
  542. }
  543. MYVERIFY(FALSE != EndDialog(hDlg, iUiChoices));
  544. return (TRUE);
  545. case IDCANCEL:
  546. MYVERIFY(FALSE != EndDialog(hDlg, -1));
  547. return TRUE;
  548. default:
  549. break;
  550. }
  551. break;
  552. case WM_CLOSE:
  553. MYVERIFY(FALSE != EndDialog(hDlg, -1));
  554. return TRUE;
  555. default:
  556. return FALSE;
  557. }
  558. return FALSE;
  559. }
  560. //+----------------------------------------------------------------------------
  561. //
  562. // Function: InstallCm
  563. //
  564. // Synopsis: This function calls LaunchInfSection on the appropriate
  565. // install section to install Connection Manager. It also installs
  566. // the browser files as appropriate.
  567. //
  568. // Arguments: HINSTANCE hInstance - Instance handle for strings
  569. // LPCTSTR szInfPath - Full path to the inf
  570. //
  571. // Returns: HRESULT - standard com codes, could return ERROR_SUCCESS_REBOOT_REQUIRED
  572. // so the caller must check for this case and ask for a reboot
  573. // if required.
  574. //
  575. // History: quintinb Created 8/12/98
  576. // quintinb Moved Browser file installation code here, since it is
  577. // part of the installation of CM. 10-2-98
  578. //
  579. //+----------------------------------------------------------------------------
  580. HRESULT InstallCm(HINSTANCE hInstance, LPCTSTR szInfPath)
  581. {
  582. HRESULT hr = E_UNEXPECTED;
  583. MYDBGASSERT((szInfPath) && (TEXT('\0') != szInfPath[0]));
  584. //
  585. // Load the Cmstp Title just in case we need to show error messages.
  586. //
  587. TCHAR szTitle[MAX_PATH+1] = {TEXT("")};
  588. MYVERIFY(0 != LoadString(hInstance, IDS_CMSTP_TITLE, szTitle, MAX_PATH));
  589. MYDBGASSERT(TEXT('\0') != szTitle[0]);
  590. //
  591. // Make sure that the Inf File exists
  592. //
  593. if (!FileExists(szInfPath))
  594. {
  595. CMTRACE1(TEXT("InstallCm -- Can't find %s, the inputted Inf file."), szInfPath);
  596. return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  597. }
  598. CPlatform plat;
  599. TCHAR szInstallSection[MAX_PATH+1] = {TEXT("")};
  600. if (plat.IsNT())
  601. {
  602. MYVERIFY(CELEMS(szInstallSection) > (UINT)wsprintf(szInstallSection,
  603. TEXT("DefaultInstall_NT")));
  604. }
  605. else
  606. {
  607. MYVERIFY(CELEMS(szInstallSection) > (UINT)wsprintf(szInstallSection,
  608. TEXT("DefaultInstall")));
  609. }
  610. hr = LaunchInfSection(szInfPath, szInstallSection, szTitle, TRUE); // bQuiet = TRUE
  611. return hr;
  612. }
  613. //+----------------------------------------------------------------------------
  614. //
  615. // Function: InstallWhistlerCmOnWin2k
  616. //
  617. // Synopsis: This function uses the CM exception inf (cmexcept.inf) to install
  618. // the Whistler CM binaries on Win2k.
  619. //
  620. // Arguments: LPCSTR pszSourceDir - source directory for cmexcept.inf and CM
  621. // binaries, including the trailing slash.
  622. //
  623. // Returns: HRESULT - standard com codes, could return ERROR_SUCCESS_REBOOT_REQUIRED
  624. // which means the caller needs to request a reboot.
  625. //
  626. // History: quintinb Created 02/09/2001
  627. //
  628. //+----------------------------------------------------------------------------
  629. HRESULT InstallWhistlerCmOnWin2k(LPCSTR pszSourceDir)
  630. {
  631. CPlatform cmplat;
  632. HRESULT hr = E_UNEXPECTED;
  633. LPSTR pszInfFile = NULL;
  634. LPCSTR c_pszExceptionInf = "cmexcept.inf";
  635. LPCSTR c_pszInstallSection = "DefaultInstall";
  636. LPCSTR c_pszUnInstallSection = "DefaultUninstall_NoPrompt";
  637. if (cmplat.IsNT5())
  638. {
  639. if (pszSourceDir && pszSourceDir[0])
  640. {
  641. DWORD dwSize = sizeof(CHAR)*(lstrlenA(pszSourceDir) + lstrlenA(c_pszExceptionInf) + 1);
  642. pszInfFile = (LPSTR)CmMalloc(dwSize);
  643. if (pszInfFile)
  644. {
  645. wsprintf(pszInfFile, "%s%s", pszSourceDir, c_pszExceptionInf);
  646. if (FileExists(pszInfFile))
  647. {
  648. hr = CallLaunchInfSectionEx(pszInfFile, c_pszInstallSection, (ALINF_BKINSTALL | ALINF_QUIET));
  649. if (FAILED(hr))
  650. {
  651. CMTRACE1(TEXT("InstallWhistlerCmOnWin2k -- CallLaunchInfSectionEx failed with hr=0x%x"), hr);
  652. HRESULT hrTemp = CallLaunchInfSectionEx(pszInfFile, c_pszUnInstallSection, (ALINF_ROLLBKDOALL | ALINF_QUIET));
  653. CMTRACE1(TEXT("InstallWhistlerCmOnWin2k -- Rolling back. CallLaunchInfSectionEx returned hr=0x%x"), hrTemp);
  654. }
  655. }
  656. else
  657. {
  658. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  659. }
  660. }
  661. else
  662. {
  663. hr = E_OUTOFMEMORY;
  664. }
  665. }
  666. else
  667. {
  668. hr = E_INVALIDARG;
  669. }
  670. }
  671. else
  672. {
  673. hr = HRESULT_FROM_WIN32(ERROR_INSTALL_PLATFORM_UNSUPPORTED); // kind of a double use of this error
  674. }
  675. CmFree(pszInfFile);
  676. return hr;
  677. }
  678. //+----------------------------------------------------------------------------
  679. //
  680. // Function: UpdateCmpDataFromExistingProfile
  681. //
  682. // Synopsis: This function enumerates all of the keys in all of the sections
  683. // of an existing cmp file and copies them to the cmp file to be
  684. // installed. This function copies all of the data in the existing
  685. // cmp unless that data already exists in the cmp to install. This
  686. // allows Admins to preseed cmp files and have their settings override
  687. // what the user currently has in their cmp.
  688. //
  689. // Arguments: LPCTSTR pszShortServiceName - Short Service name of the profile
  690. // LPCTSTR szCurrentCmp - Full path to the currently installed cmp
  691. // LPCTSTR szCmpToBeInstalled - Full path to the cmp to install
  692. //
  693. // Returns: BOOL - TRUE if the cmp is copied and updated properly
  694. //
  695. // History: quintinb Created 03/16/99
  696. // quintinb rewrote for Whistler bug 18021 03/05/00
  697. //
  698. //+----------------------------------------------------------------------------
  699. BOOL UpdateCmpDataFromExistingProfile(LPCTSTR pszShortServiceName, LPCTSTR pszCurrentCmp, LPCTSTR pszCmpToBeInstalled)
  700. {
  701. if((NULL == pszShortServiceName) && (TEXT('\0') == pszShortServiceName[0]) &&
  702. (NULL == pszCurrentCmp) && (TEXT('\0') == pszCurrentCmp[0]) &&
  703. (NULL == pszCmpToBeInstalled) && (TEXT('\0') == pszCmpToBeInstalled[0]))
  704. {
  705. CMASSERTMSG(FALSE, TEXT("UpdateCmpDataFromExistingProfile -- Invalid parameter."));
  706. return FALSE;
  707. }
  708. BOOL bReturn = FALSE;
  709. BOOL bExitLoop = FALSE;
  710. DWORD dwSize = MAX_PATH;
  711. DWORD dwReturnedSize;
  712. LPTSTR pszAllSections = NULL;
  713. LPTSTR pszAllKeysInCurrentSection = NULL;
  714. LPTSTR pszCurrentSection = NULL;
  715. LPTSTR pszCurrentKey = NULL;
  716. TCHAR szData[MAX_PATH+1];
  717. //
  718. // First lets get all of the sections from the existing cmp
  719. //
  720. pszAllSections = (TCHAR*)CmMalloc(dwSize*sizeof(TCHAR));
  721. do
  722. {
  723. MYDBGASSERT(pszAllSections);
  724. if (pszAllSections)
  725. {
  726. dwReturnedSize = GetPrivateProfileString(NULL, NULL, TEXT(""), pszAllSections, dwSize, pszCurrentCmp);
  727. if (dwReturnedSize == (dwSize - 2))
  728. {
  729. //
  730. // The buffer is too small, lets allocate a bigger one
  731. //
  732. dwSize = 2*dwSize;
  733. if (dwSize > 1024*1024)
  734. {
  735. CMASSERTMSG(FALSE, TEXT("UpdateCmpDataFromExistingProfile -- Allocation above 1MB, bailing out."));
  736. goto exit;
  737. }
  738. pszAllSections = (TCHAR*)CmRealloc(pszAllSections, dwSize*sizeof(TCHAR));
  739. }
  740. else if (0 == dwReturnedSize)
  741. {
  742. //
  743. // We got an error, lets exit.
  744. //
  745. CMASSERTMSG(FALSE, TEXT("UpdateCmpDataFromExistingProfile -- GetPrivateProfileString returned failure."));
  746. goto exit;
  747. }
  748. else
  749. {
  750. bExitLoop = TRUE;
  751. }
  752. }
  753. else
  754. {
  755. goto exit;
  756. }
  757. } while (!bExitLoop);
  758. //
  759. // Okay, now we have all of the sections in the existing cmp file. Lets enumerate
  760. // all of the keys in each section and see which ones need to be copied over.
  761. //
  762. pszCurrentSection = pszAllSections;
  763. dwSize = MAX_PATH;
  764. pszAllKeysInCurrentSection = (TCHAR*)CmMalloc(dwSize*sizeof(TCHAR));
  765. while (TEXT('\0') != pszCurrentSection[0])
  766. {
  767. //
  768. // Get all of the keys in the current section
  769. //
  770. bExitLoop = FALSE;
  771. do
  772. {
  773. if (pszAllKeysInCurrentSection)
  774. {
  775. dwReturnedSize = GetPrivateProfileString(pszCurrentSection, NULL, TEXT(""), pszAllKeysInCurrentSection,
  776. dwSize, pszCurrentCmp);
  777. if (dwReturnedSize == (dwSize - 2))
  778. {
  779. //
  780. // The buffer is too small, lets allocate a bigger one
  781. //
  782. dwSize = 2*dwSize;
  783. if (dwSize > 1024*1024)
  784. {
  785. CMASSERTMSG(FALSE, TEXT("UpdateCmpDataFromExistingProfile -- Allocation above 1MB, bailing out."));
  786. goto exit;
  787. }
  788. pszAllKeysInCurrentSection = (TCHAR*)CmRealloc(pszAllKeysInCurrentSection, dwSize*sizeof(TCHAR));
  789. }
  790. else if (0 == dwReturnedSize)
  791. {
  792. //
  793. // We got an error, lets exit.
  794. //
  795. CMASSERTMSG(FALSE, TEXT("UpdateCmpDataFromExistingProfile -- GetPrivateProfileString returned failure."));
  796. goto exit;
  797. }
  798. else
  799. {
  800. bExitLoop = TRUE;
  801. }
  802. }
  803. else
  804. {
  805. goto exit;
  806. }
  807. } while (!bExitLoop);
  808. //
  809. // Now process all of the keys in the current section
  810. //
  811. pszCurrentKey = pszAllKeysInCurrentSection;
  812. while (TEXT('\0') != pszCurrentKey[0])
  813. {
  814. //
  815. // Try to get the value of the key from the new cmp. If it
  816. // doesn't exist, then copy of the old cmp value. If it
  817. // does exist keep the new cmp value and ignore the old one.
  818. //
  819. dwReturnedSize = GetPrivateProfileString(pszCurrentSection, pszCurrentKey, TEXT(""),
  820. szData, MAX_PATH, pszCmpToBeInstalled);
  821. if (0 == dwReturnedSize)
  822. {
  823. //
  824. // Then we have a value in the old profile that we don't have in the new profile.
  825. //
  826. dwReturnedSize = GetPrivateProfileString(pszCurrentSection, pszCurrentKey, TEXT(""),
  827. szData, MAX_PATH, pszCurrentCmp);
  828. if (dwReturnedSize)
  829. {
  830. MYVERIFY(0 != WritePrivateProfileString(pszCurrentSection, pszCurrentKey, szData, pszCmpToBeInstalled));
  831. }
  832. }
  833. //
  834. // Advance to the next key in pszAllKeysInCurrentSection
  835. //
  836. pszCurrentKey = pszCurrentKey + lstrlen(pszCurrentKey) + 1;
  837. }
  838. //
  839. // Now advance to the next string in pszAllSections
  840. //
  841. pszCurrentSection = pszCurrentSection + lstrlen(pszCurrentSection) + 1;
  842. }
  843. //
  844. // Flush the updated cmp
  845. //
  846. WritePrivateProfileString(NULL, NULL, NULL, pszCmpToBeInstalled); //lint !e534 this call will return 0
  847. bReturn = TRUE;
  848. exit:
  849. CmFree(pszAllSections);
  850. CmFree(pszAllKeysInCurrentSection);
  851. return bReturn;
  852. }
  853. //+----------------------------------------------------------------------------
  854. //
  855. // Function: MigrateCmpData
  856. //
  857. // Synopsis: This function checks to see if a profile of the same long service
  858. // and short service name is already installed. If it is, it migrates
  859. // the existing cmp data to the cmp file that is to be installed.
  860. // If the same piece of data exists in both profiles the data in the
  861. // cmp to be installed wins (allows admins to pre-seed data in the
  862. // cmp and override what users have picked).
  863. //
  864. // Arguments: HINSTANCE hInstance - Instance handle for string resources
  865. // BOOL bInstallForAllUsers - whether this is an all users profile or not
  866. // LPCTSTR pszServiceName - ServiceName of the current profile
  867. // LPCTSTR pszShortServiceName - Short Service name of the current profile
  868. // BOOL bSilent - whether messages to the user can be displayed or not
  869. //
  870. // Returns: int - returns -1 on error, otherwise TRUE or FALSE depending on if a same name
  871. // profile was discovered
  872. //
  873. // History: quintinb Created 9/8/98
  874. //
  875. //+----------------------------------------------------------------------------
  876. BOOL MigrateCmpData(HINSTANCE hInstance, BOOL bInstallForAllUsers, LPCTSTR pszServiceName,
  877. LPCTSTR pszShortServiceName, BOOL bSilent)
  878. {
  879. //
  880. // Check the parameters
  881. //
  882. if ((NULL == pszShortServiceName) || (TEXT('\0') == pszShortServiceName[0]) ||
  883. (NULL == pszServiceName) || (TEXT('\0') == pszServiceName[0]))
  884. {
  885. CMASSERTMSG(FALSE, TEXT("MigrateCmpData -- Invalid Parameter"));
  886. return FALSE;
  887. }
  888. BOOL bReturn = TRUE;
  889. DWORD dwSize = MAX_PATH;
  890. HKEY hKey = NULL;
  891. HKEY hBaseKey = bInstallForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
  892. TCHAR szExistingCmp[MAX_PATH+1];
  893. TCHAR szCmpToBeInstalled[MAX_PATH+1];
  894. TCHAR szFmtString[2*MAX_PATH+1] = TEXT("");
  895. TCHAR szMsg[2*MAX_PATH+1] = TEXT("");
  896. //
  897. // Read the mappings value
  898. //
  899. LONG lResult = RegOpenKeyEx(hBaseKey, c_pszRegCmMappings, 0, KEY_READ, &hKey);
  900. if (ERROR_SUCCESS == lResult)
  901. {
  902. lResult = RegQueryValueEx(hKey, pszServiceName, NULL, NULL, (LPBYTE)szFmtString, &dwSize);
  903. if (ERROR_SUCCESS == lResult)
  904. {
  905. //
  906. // Expand the path in case it contains environment vars
  907. //
  908. if (0 == ExpandEnvironmentStrings(szFmtString, szExistingCmp, MAX_PATH))
  909. {
  910. CMASSERTMSG(FALSE, TEXT("MigrateCmpData -- Unable to expand environment strings, not migrating cmp data."));
  911. goto exit;
  912. }
  913. //
  914. // If the file doesn't exist we have nothing to get cmp settings from ... thus
  915. // lets just happily exit.
  916. //
  917. if (!FileExists(szExistingCmp))
  918. {
  919. goto exit;
  920. }
  921. //
  922. // Check to make sure that the Short Service Names agree for the two profiles
  923. //
  924. CFileNameParts ExistingCmpParts(szExistingCmp);
  925. if (0 != lstrcmpi(pszShortServiceName, ExistingCmpParts.m_FileName))
  926. {
  927. if (!bSilent)
  928. {
  929. MYVERIFY(0 != LoadString(hInstance, IDS_SAME_LS_DIFF_SS, szFmtString, CELEMS(szFmtString)));
  930. MYVERIFY(CELEMS(szMsg) > (UINT)wsprintf(szMsg, szFmtString, pszShortServiceName,
  931. ExistingCmpParts.m_FileName, pszServiceName));
  932. MessageBox(NULL, szMsg, pszServiceName, MB_OK);
  933. }
  934. bReturn = -1;
  935. goto exit;
  936. }
  937. //
  938. // Get the path of the cmp to install
  939. //
  940. MYVERIFY(CELEMS(szCmpToBeInstalled) > (UINT)wsprintf(szCmpToBeInstalled,
  941. TEXT("%s%s.cmp"), g_szProfileSourceDir, pszShortServiceName));
  942. if (FALSE == UpdateCmpDataFromExistingProfile(pszShortServiceName, szExistingCmp, szCmpToBeInstalled))
  943. {
  944. bReturn = -1;
  945. }
  946. }
  947. }
  948. exit:
  949. if (hKey)
  950. {
  951. RegCloseKey(hKey);
  952. }
  953. return bReturn;
  954. }
  955. //+----------------------------------------------------------------------------
  956. //
  957. // Function: NeedCM10Upgrade
  958. //
  959. // Synopsis: This function detects and prepares data for the same name upgrade of a CM 1.0 profile.
  960. // Thus if you pass in a short service name and a service name, the
  961. // function detects if this profile is already installed for all users.
  962. // If it is, then the function checks the profile version stamps in the cmp.
  963. // If the current version isn't already newer and the user isn't a non-admin
  964. // on NT5, then we prompt the user if they want to upgrade the current install.
  965. // If they choose to upgrade then this function migrates the cmp data and
  966. // finds the uninstall inf.
  967. //
  968. // Arguments: HINSTANCE hInstance - Instance handle for string resources
  969. // LPCTSTR pszServiceName - ServiceName of the current profile
  970. // LPCTSTR pszShortServiceName - Short Service name of the current profile
  971. // LPTSTR pszOldInfPath - Out param for the old inf path, if the same name
  972. // upgrade is needed.
  973. //
  974. // Returns: int - returns -1 on error, otherwise TRUE or FALSE depending on if a same name
  975. // profile was discovered
  976. //
  977. // History: quintinb Created 9/8/98
  978. //
  979. //+----------------------------------------------------------------------------
  980. int NeedCM10Upgrade(HINSTANCE hInstance, LPCTSTR pszServiceName, LPCTSTR pszShortServiceName,
  981. LPTSTR pszOldInfPath, BOOL bSilent, CPlatform* plat)
  982. {
  983. HKEY hKey = NULL;
  984. int iReturn = FALSE;
  985. TCHAR szFmtString[2*MAX_PATH+1] = TEXT("");
  986. TCHAR szMsg[2*MAX_PATH+1] = TEXT("");
  987. const int c_iCM12ProfileVersion = 4;
  988. MYDBGASSERT((NULL != pszShortServiceName) && (TEXT('\0') != pszShortServiceName[0]));
  989. MYDBGASSERT((NULL != pszServiceName) && (TEXT('\0') != pszServiceName[0]));
  990. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_pszRegCmMappings, 0,
  991. KEY_READ, &hKey))
  992. {
  993. int iCurrentCmpVersion;
  994. int iCmpVersionToInstall;
  995. TCHAR szCurrentCmp[MAX_PATH+1];
  996. TCHAR szCmpToBeInstalled[MAX_PATH+1];
  997. DWORD dwSize = MAX_PATH;
  998. if (ERROR_SUCCESS == RegQueryValueEx(hKey, pszServiceName, NULL,
  999. NULL, (LPBYTE)szCurrentCmp, &dwSize))
  1000. {
  1001. //
  1002. // First check to see that the file really exists. It is a somewhat probable case
  1003. // that the users will have deleted their Profile files but left the registry
  1004. // keys intact (they didn't uninstall it). In fact, MSN 2.5 and 2.6 operate this
  1005. // way. Thus if the cmp doesn't actually exist then we don't need a same name
  1006. // upgrade.
  1007. //
  1008. if (!FileExists(szCurrentCmp))
  1009. {
  1010. CMASSERTMSG(FALSE, TEXT("Detected a CM 1.0 Upgrade, but the cmp didn't exist. Not Processing the upgrade."));
  1011. iReturn = FALSE;
  1012. goto exit;
  1013. }
  1014. //
  1015. // Check to make sure that the Short Service Names agree for the two profiles
  1016. //
  1017. CFileNameParts CurrentCmpParts(szCurrentCmp);
  1018. if (0 != lstrcmpi(pszShortServiceName, CurrentCmpParts.m_FileName))
  1019. {
  1020. if (!bSilent)
  1021. {
  1022. MYVERIFY(0 != LoadString(hInstance, IDS_SAME_LS_DIFF_SS, szFmtString, CELEMS(szFmtString)));
  1023. MYVERIFY(CELEMS(szMsg) > (UINT)wsprintf(szMsg, szFmtString, pszShortServiceName,
  1024. CurrentCmpParts.m_FileName, pszServiceName));
  1025. MessageBox(NULL, szMsg, pszServiceName, MB_OK);
  1026. }
  1027. iReturn = -1;
  1028. goto exit;
  1029. }
  1030. //
  1031. // Then we have the same servicename profile installed as an all users install.
  1032. // Check the version number in the CMP. If the same version or less then we want
  1033. // to run the same name upgrade. If the current version is more recent, then
  1034. // we want to prevent the user from installing.
  1035. //
  1036. //
  1037. // Get Currently Installed Profile version
  1038. //
  1039. iCurrentCmpVersion = GetPrivateProfileInt(c_pszCmSectionProfileFormat, c_pszVersion,
  1040. 0, szCurrentCmp);
  1041. //
  1042. // Get the version of the profile to install
  1043. //
  1044. MYVERIFY(CELEMS(szCmpToBeInstalled) > (UINT)wsprintf(szCmpToBeInstalled,
  1045. TEXT("%s%s.cmp"), g_szProfileSourceDir, pszShortServiceName));
  1046. iCmpVersionToInstall = GetPrivateProfileInt(c_pszCmSectionProfileFormat, c_pszVersion, 0,
  1047. szCmpToBeInstalled);
  1048. if (iCurrentCmpVersion > iCmpVersionToInstall)
  1049. {
  1050. //
  1051. // We must not allow the install because a newer version of the profile format
  1052. // is already installed.
  1053. //
  1054. if (!bSilent)
  1055. {
  1056. MYVERIFY(0 != LoadString(hInstance, IDS_NEWER_SAMENAME, szFmtString, CELEMS(szFmtString)));
  1057. MYVERIFY(CELEMS(szMsg) > (UINT)wsprintf(szMsg, szFmtString, pszServiceName));
  1058. MessageBox(NULL, szMsg, pszServiceName, MB_OK | MB_TOPMOST | MB_SYSTEMMODAL);
  1059. }
  1060. iReturn = -1;
  1061. goto exit;
  1062. }
  1063. else if (iCurrentCmpVersion < c_iCM12ProfileVersion)
  1064. {
  1065. int iUserSelection = 0;
  1066. //
  1067. // Make sure that this isn't a Non-Admin NT5 person trying to install
  1068. //
  1069. if (plat->IsAtLeastNT5() && !IsAdmin())
  1070. {
  1071. CMASSERTMSG(!bSilent, TEXT("NeedCM10Upgrade -- NonAdmin trying to Same Name upgrade a profile, exiting!"));
  1072. if (!bSilent)
  1073. {
  1074. MYVERIFY(0 != LoadString(hInstance, IDS_GET_ADMIN, szFmtString, CELEMS(szFmtString)));
  1075. MYVERIFY(CELEMS(szMsg) > (UINT)wsprintf(szMsg, szFmtString, pszServiceName));
  1076. MessageBox(NULL, szMsg, pszServiceName, MB_OK);
  1077. }
  1078. iReturn = -1;
  1079. goto exit;
  1080. }
  1081. else
  1082. {
  1083. //
  1084. // Now prompt the user to make sure that they want to go ahead with the upgrade
  1085. //
  1086. if (!bSilent)
  1087. {
  1088. MYVERIFY(0 != LoadString(hInstance, IDS_UPGRADE_SAMENAME, szFmtString, CELEMS(szFmtString)));
  1089. MYVERIFY(CELEMS(szMsg) > (UINT)wsprintf(szMsg, szFmtString, pszServiceName));
  1090. iUserSelection = MessageBox(NULL, szMsg, pszServiceName, MB_YESNO | MB_TOPMOST | MB_SYSTEMMODAL);
  1091. }
  1092. else
  1093. {
  1094. //
  1095. // Assume yes with Silent Same Name Upgrade
  1096. //
  1097. iUserSelection = IDYES;
  1098. }
  1099. }
  1100. if (IDYES == iUserSelection)
  1101. {
  1102. if (UpdateCmpDataFromExistingProfile(pszShortServiceName, szCurrentCmp, szCmpToBeInstalled))
  1103. {
  1104. CFileNameParts FileParts(szCurrentCmp);
  1105. if (0 != GetSystemDirectory(szFmtString, MAX_PATH)) // use szFmtString as a temp
  1106. {
  1107. MYVERIFY(CELEMS(szMsg) > (UINT)wsprintf(szMsg, TEXT("%s\\%s.inf"), szFmtString, FileParts.m_FileName));
  1108. if (FileExists(szMsg))
  1109. {
  1110. lstrcpy(pszOldInfPath, szMsg);
  1111. }
  1112. else
  1113. {
  1114. //
  1115. // Not in the system directory, try profile dir.
  1116. //
  1117. MYVERIFY(CELEMS(szMsg) > (UINT)wsprintf(szMsg,
  1118. TEXT("%s%s%s\\%s.inf"), FileParts.m_Drive,
  1119. FileParts.m_Dir, FileParts.m_FileName,
  1120. FileParts.m_FileName));
  1121. if (FileExists(szMsg))
  1122. {
  1123. lstrcpy(pszOldInfPath, szMsg);
  1124. }
  1125. else
  1126. {
  1127. CMASSERTMSG(FALSE, TEXT("Unable to locate the profile INF -- old profile won't be uninstalled but installation will continue."));
  1128. pszOldInfPath[0] = TEXT('\0');
  1129. }
  1130. }
  1131. }
  1132. }
  1133. else
  1134. {
  1135. CMASSERTMSG(FALSE, TEXT("Couldn't copy cmp file for same name upgrade. Exiting."));
  1136. iReturn = -1;
  1137. goto exit;
  1138. }
  1139. iReturn = TRUE;
  1140. goto exit;
  1141. }
  1142. else
  1143. {
  1144. iReturn = -1;
  1145. goto exit;
  1146. }
  1147. }
  1148. else
  1149. {
  1150. //
  1151. // Then either the version numbers are the same or the version to install is newer but
  1152. // the existing profile is at least a 1.2 profile.
  1153. //
  1154. iReturn = FALSE;
  1155. goto exit;
  1156. }
  1157. }
  1158. }
  1159. exit:
  1160. if (hKey)
  1161. {
  1162. MYVERIFY(ERROR_SUCCESS == RegCloseKey(hKey));
  1163. }
  1164. return iReturn;
  1165. }
  1166. //+----------------------------------------------------------------------------
  1167. //
  1168. // Function: MeetsMinimumProfileInstallVersion
  1169. //
  1170. // Synopsis: Because of problems with previous profile installers (namely 1.0),
  1171. // we built in minimum install requirements for profiles. Thus we
  1172. // look under the Connection Manager App Paths key for a minimum profile
  1173. // version, a minimum build number, and a minimum profile format version.
  1174. // If the profile trying to install doesn't meet any of these requirements,
  1175. // then the function returns FALSE and the install is failed.
  1176. //
  1177. // Arguments: DWORD dwInstallerVersionNumber - current installer version number
  1178. // DWORD dwInstallerBuildNumber - current installer build number
  1179. // LPCTSTR pszInfFile - path to the inf to get the profile format version number
  1180. //
  1181. // Returns: BOOL - TRUE if all the version requirements are met
  1182. //
  1183. // History: quintinb Created Header 5/24/99
  1184. //
  1185. //+----------------------------------------------------------------------------
  1186. BOOL MeetsMinimumProfileInstallVersion(DWORD dwInstallerVersionNumber,
  1187. DWORD dwInstallerBuildNumber, LPCTSTR pszInfFile)
  1188. {
  1189. const TCHAR* const c_pszRegMinProfileVersion = TEXT("MinProfileVersion");
  1190. const TCHAR* const c_pszRegMinProfileBuildNum = TEXT("MinProfileBuildNum");
  1191. const TCHAR* const c_pszRegMinProfileFmtVersion = TEXT("MinProfileFmtVersion");
  1192. HKEY hKey = NULL;
  1193. BOOL bReturn = TRUE;
  1194. DWORD dwTemp;
  1195. //
  1196. // First check the format version.
  1197. //
  1198. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_pszRegCmAppPaths, 0, KEY_READ, &hKey))
  1199. {
  1200. DWORD dwSize = sizeof(DWORD);
  1201. DWORD dwType = 0;
  1202. if (ERROR_SUCCESS == RegQueryValueEx(hKey, c_pszRegMinProfileFmtVersion, NULL,
  1203. &dwType, (LPBYTE)&dwTemp, &dwSize))
  1204. {
  1205. //
  1206. // Get the profile format version from the cmp file
  1207. //
  1208. DWORD dwFormatVersion;
  1209. CFileNameParts InfParts(pszInfFile);
  1210. TCHAR szCmpFile[MAX_PATH+1];
  1211. MYVERIFY(CELEMS(szCmpFile) > (UINT)wsprintf(szCmpFile, TEXT("%s%s%s%s"),
  1212. InfParts.m_Drive, InfParts.m_Dir, InfParts.m_FileName, c_pszCmpExt));
  1213. dwFormatVersion = (DWORD)GetPrivateProfileInt(c_pszCmSectionProfileFormat,
  1214. c_pszVersion, -1, szCmpFile);
  1215. if (dwTemp > dwFormatVersion)
  1216. {
  1217. bReturn = FALSE;
  1218. goto exit;
  1219. }
  1220. }
  1221. //
  1222. // Next Check the profile version (equivalent to the version number of the
  1223. // CM bits the profile was built with)
  1224. //
  1225. dwSize = sizeof(DWORD);
  1226. if (ERROR_SUCCESS == RegQueryValueEx(hKey, c_pszRegMinProfileVersion, NULL,
  1227. &dwType, (LPBYTE)&dwTemp, &dwSize))
  1228. {
  1229. //
  1230. // If the minimum version number from the registry is higher than the
  1231. // version number listed here, fail the install.
  1232. //
  1233. if (dwTemp > dwInstallerVersionNumber)
  1234. {
  1235. bReturn = FALSE;
  1236. goto exit;
  1237. }
  1238. }
  1239. //
  1240. // Next Check the profile build number (equivalent to the build number of the
  1241. // CM bits the profile was built with)
  1242. //
  1243. dwSize = sizeof(DWORD);
  1244. if (ERROR_SUCCESS == RegQueryValueEx(hKey, c_pszRegMinProfileBuildNum, NULL,
  1245. &dwType, (LPBYTE)&dwTemp, &dwSize))
  1246. {
  1247. //
  1248. // If the minimum version number from the registry is higher than the
  1249. // version number listed here, fail the install.
  1250. //
  1251. if (dwTemp > dwInstallerBuildNumber)
  1252. {
  1253. bReturn = FALSE;
  1254. goto exit;
  1255. }
  1256. }
  1257. }
  1258. exit:
  1259. if (hKey)
  1260. {
  1261. MYVERIFY(ERROR_SUCCESS == RegCloseKey(hKey));
  1262. }
  1263. return bReturn;
  1264. }
  1265. //+----------------------------------------------------------------------------
  1266. //
  1267. // Function: UninstallExistingCmException
  1268. //
  1269. // Synopsis: This function looks for the cmexcept.inf file in the %windir%\inf
  1270. // directory. If this file exists, then we uninstall the
  1271. // existing exception before we install the new one. This prevents
  1272. // the rollback information from being lost.
  1273. //
  1274. // Arguments: none
  1275. //
  1276. // Returns: BOOL - returns TRUE if the installer needs to uninstall the
  1277. // existing CM exception install and FALSE if the install
  1278. // can continue without it.
  1279. //
  1280. // History: quintinb Created 11/1/98
  1281. //
  1282. //+----------------------------------------------------------------------------
  1283. HRESULT UninstallExistingCmException()
  1284. {
  1285. HRESULT hr = E_UNEXPECTED;
  1286. LPCSTR c_pszCmExceptInfRelative = TEXT("\\Inf\\cmexcept.inf");
  1287. LPCSTR c_pszUnInstallSection = "DefaultUninstall_NoPrompt";
  1288. UINT uNumChars = GetWindowsDirectoryA(NULL, 0);
  1289. if (uNumChars)
  1290. {
  1291. uNumChars = uNumChars + lstrlenA(c_pszCmExceptInfRelative);
  1292. LPSTR pszPathToCmExceptInf = (LPSTR)CmMalloc(sizeof(CHAR)*(uNumChars + 1));
  1293. if (pszPathToCmExceptInf)
  1294. {
  1295. if (GetWindowsDirectoryA(pszPathToCmExceptInf, uNumChars))
  1296. {
  1297. lstrcatA(pszPathToCmExceptInf, c_pszCmExceptInfRelative);
  1298. if (FileExists(pszPathToCmExceptInf))
  1299. {
  1300. //
  1301. // We have an exception inf in the directory so we need to uninstall it. Were the
  1302. // bits already on the machine newer than the bits we have in the cab, then we wouldn't
  1303. // be installing. If the bits on the machine don't match the version that the inf says,
  1304. // then we are better uninstalling those bits and putting them in a known state anyway.
  1305. //
  1306. hr = CallLaunchInfSectionEx(pszPathToCmExceptInf, c_pszUnInstallSection, ALINF_ROLLBKDOALL);
  1307. CMTRACE1(TEXT("UninstallExistingCmException -- CM Exception inf found, uninstalling. CallLaunchInfSectionEx returned hr=0x%x"), hr);
  1308. }
  1309. else
  1310. {
  1311. hr = S_FALSE; // nothing to delete
  1312. }
  1313. }
  1314. CmFree(pszPathToCmExceptInf);
  1315. }
  1316. else
  1317. {
  1318. hr = E_OUTOFMEMORY;
  1319. }
  1320. }
  1321. return hr;
  1322. }
  1323. //+----------------------------------------------------------------------------
  1324. //
  1325. // Function: CheckCmAndIeRequirements
  1326. //
  1327. // Synopsis: This function checks the CM and IE requirements for a profile
  1328. // and returns whether the CM should be installed, whether profile
  1329. // migration should occur, and most importantly if the install should
  1330. // exit now because of insufficient requirements.
  1331. //
  1332. // Arguments: BOOL* pbInstallCm - tells if CM should be installed or not
  1333. // BOOL* pbMigrateExistingProfiles - tells if profile migration should occur
  1334. // LPCTSTR szInfFile - the inf file to install
  1335. // LPCTSTR szServiceName - The Service name, used as a title
  1336. //
  1337. // Returns: BOOL - returns TRUE if the install should continue, FALSE if
  1338. // if the install should be failed.
  1339. //
  1340. // History: quintinb Created 11/1/98
  1341. //
  1342. //+----------------------------------------------------------------------------
  1343. BOOL CheckCmAndIeRequirements(HINSTANCE hInstance, BOOL* pbInstallCm,
  1344. BOOL* pbMigrateExistingProfiles, LPCTSTR szInfFile,
  1345. BOOL bNoSupportFiles, LPCTSTR szServiceName, BOOL bSilent)
  1346. {
  1347. CmVersion CmVer;
  1348. CPlatform plat;
  1349. BOOL bReturn;
  1350. BOOL bCMRequired;
  1351. TCHAR szMsg[2*MAX_PATH+1];
  1352. TCHAR szTemp[MAX_PATH+1];
  1353. TCHAR szString[MAX_PATH+1];
  1354. DWORD dwInstallerBuildNumber = 0;
  1355. DWORD dwInstallerVersionNumber = 0;
  1356. //
  1357. // The inf file tells us if we included the CM bits
  1358. //
  1359. if (plat.IsNT5())
  1360. {
  1361. //
  1362. // We now need to check to see if we need to install the Windows XP bits on
  1363. // Windows 2000. Thus we check the inf to see if this profile includes the CM
  1364. // bits or not. Note that we never want to install the IE support files on
  1365. // Win2k so set bIERequired to TRUE.
  1366. //
  1367. bCMRequired = !GetPrivateProfileInt(c_pszCmakStatus, TEXT("IncludeCMCode"), 0, szInfFile);
  1368. }
  1369. else if (CmIsNative())
  1370. {
  1371. //
  1372. // CM and IE are required on Windows XP and any platforms with the Native
  1373. // regkey set except NT5 and Win98 SE as they are special cases.
  1374. //
  1375. bCMRequired = TRUE;
  1376. }
  1377. else
  1378. {
  1379. bCMRequired = !GetPrivateProfileInt(c_pszCmakStatus, TEXT("IncludeCMCode"),
  1380. 0, szInfFile);
  1381. }
  1382. //
  1383. // Now try to get the version numbers from the profile INF
  1384. //
  1385. dwInstallerBuildNumber = (DWORD)GetPrivateProfileInt(c_pszSectionCmDial32,
  1386. c_pszVerBuild, ((VER_PRODUCTBUILD << c_iShiftAmount) + VER_PRODUCTBUILD_QFE),
  1387. szInfFile);
  1388. dwInstallerVersionNumber = (DWORD)GetPrivateProfileInt(c_pszSectionCmDial32,
  1389. c_pszVersion, (HIBYTE(VER_PRODUCTVERSION_W) << c_iShiftAmount) + (LOBYTE(VER_PRODUCTVERSION_W)),
  1390. szInfFile);
  1391. //
  1392. // First check to see if we have any install minimums in the registry. If these
  1393. // minimums exist and our profile doesn't meet those minimums then we must
  1394. // throw an error message and exit.
  1395. //
  1396. if (!MeetsMinimumProfileInstallVersion(dwInstallerVersionNumber,
  1397. dwInstallerBuildNumber, szInfFile))
  1398. {
  1399. if (!bSilent)
  1400. {
  1401. MYVERIFY(0 != LoadString(hInstance, IDS_PROFILE_TOO_OLD, szMsg, MAX_PATH));
  1402. MessageBox(NULL, szMsg, szServiceName, MB_OK | MB_ICONERROR);
  1403. }
  1404. return FALSE;
  1405. }
  1406. //
  1407. // Should we migrate existing profiles? Always try to migrate if we find all user
  1408. // profiles already on the machine.
  1409. //
  1410. *pbMigrateExistingProfiles = AllUserProfilesInstalled();
  1411. //
  1412. // Do CM bits exist on the machine?
  1413. //
  1414. if (CmVer.IsPresent())
  1415. {
  1416. if ((dwInstallerVersionNumber < CmVer.GetVersionNumber()) ||
  1417. (dwInstallerBuildNumber < CmVer.GetBuildAndQfeNumber()))
  1418. {
  1419. //
  1420. // If the CM bits on the machine are newer than the bits we have in the cab,
  1421. // then we only want to install the profile files and not the CM bits themselves.
  1422. //
  1423. *pbInstallCm = FALSE;
  1424. bReturn = TRUE;
  1425. }
  1426. else
  1427. {
  1428. //
  1429. // Then the CM bits on the machine are older than the bits in the cab or
  1430. // the two versions match. Either way, we should install the bits in the
  1431. // cab unless this is Win2k where we never want to re-install the same
  1432. // version of CM bits as we will lose our rollback information.
  1433. //
  1434. if (bCMRequired)
  1435. {
  1436. if ((dwInstallerVersionNumber == CmVer.GetVersionNumber()) &&
  1437. (CmVer.GetBuildNumber() > c_CmMin13Version))
  1438. {
  1439. //
  1440. // Then the builds have the same major and minor version number
  1441. // and should be considered in the same "Version Family". Note
  1442. // that we also check for a minimum build number because 7.00 is
  1443. // the version number for the CM 1.0 release in NT4 SP4 and we want CM
  1444. // profiles to not install on NT5 Beta2 Bits.
  1445. //
  1446. *pbInstallCm = FALSE;
  1447. bReturn = TRUE;
  1448. }
  1449. else
  1450. {
  1451. MYVERIFY(CELEMS(szString) > (UINT)wsprintf(szString, TEXT("%u.%u.%u.%u"),
  1452. HIWORD(dwInstallerVersionNumber), LOWORD(dwInstallerVersionNumber),
  1453. HIWORD(dwInstallerBuildNumber), LOWORD(dwInstallerBuildNumber)));
  1454. if (!bSilent)
  1455. {
  1456. MYVERIFY(0 != LoadString(hInstance, IDS_CM_OLDVERSION, szTemp, MAX_PATH));
  1457. MYVERIFY(CELEMS(szMsg) > (UINT)wsprintf(szMsg, szTemp, szString));
  1458. MessageBox(NULL, szMsg, szServiceName, MB_OK);
  1459. }
  1460. return FALSE;
  1461. }
  1462. }
  1463. else
  1464. {
  1465. if ((dwInstallerVersionNumber == CmVer.GetVersionNumber()) &&
  1466. (dwInstallerBuildNumber == CmVer.GetBuildAndQfeNumber()))
  1467. {
  1468. //
  1469. // Don't reinstall the CM bits if they are the same version
  1470. // and we are on Win2k. Doing so will overwrite the version of
  1471. // CM ready for rollback.
  1472. //
  1473. *pbInstallCm = !(plat.IsNT5());
  1474. bReturn = TRUE;
  1475. }
  1476. else if (plat.IsNT5() && (FALSE == IsAdmin()))
  1477. {
  1478. //
  1479. // If this is Win2k and we need to install the CM binaries via the exception installer,
  1480. // then the user must be an Administrator to do so. Since this user isn't, fail
  1481. // the install and give the user a warning message.
  1482. //
  1483. if (!bSilent)
  1484. {
  1485. MYVERIFY(0 != LoadString(hInstance, IDS_CANNOT_INSTALL_CM, szMsg, 2*MAX_PATH));
  1486. MessageBox(NULL, szMsg, szServiceName, MB_OK | MB_ICONEXCLAMATION);
  1487. }
  1488. return FALSE;
  1489. }
  1490. else
  1491. {
  1492. //
  1493. // If this is Win2k, we need to make sure we aren't doing a cross language install.
  1494. // Basically, we want to ensure that we aren't installing English CM bits on a native
  1495. // German machine for instance. If so, fail the install and inform the user why.
  1496. //
  1497. if (plat.IsNT5())
  1498. {
  1499. const TCHAR* const c_pszCmstp = TEXT("cmstp.exe");
  1500. CFileNameParts InfFileParts(szInfFile);
  1501. DWORD dwLen = lstrlen(InfFileParts.m_Drive) + lstrlen(InfFileParts.m_Dir) + lstrlen(c_pszCmstp);
  1502. if (MAX_PATH >= dwLen)
  1503. {
  1504. wsprintf(szTemp, TEXT("%s%s%s"), InfFileParts.m_Drive, InfFileParts.m_Dir, c_pszCmstp);
  1505. CVersion CmstpVer(szTemp);
  1506. DWORD dwExistingCmLcid = CmVer.GetLCID();
  1507. DWORD dwCmstpLcid = CmstpVer.GetLCID();
  1508. if (FALSE == ArePrimaryLangIDsEqual(dwExistingCmLcid, dwCmstpLcid))
  1509. {
  1510. if (!bSilent)
  1511. {
  1512. MYVERIFY(0 != LoadString(hInstance, IDS_CROSS_LANG_INSTALL, szMsg, 2*MAX_PATH));
  1513. MessageBox(NULL, szMsg, szServiceName, MB_OK | MB_ICONEXCLAMATION);
  1514. }
  1515. return FALSE;
  1516. }
  1517. }
  1518. }
  1519. //
  1520. // Now check to see if installing CM is going to have an effect on CMAK
  1521. //
  1522. CmakVersion CmakVer;
  1523. if (CmakVer.Is121Cmak() || CmakVer.Is122Cmak())
  1524. {
  1525. //
  1526. // Then the Win2k or IEAK5 version of CMAK is installed. Installing
  1527. // the Whistler version of CM will break this version of CMAK. We
  1528. // need to ask the user if they wish to continue the install and break
  1529. // CMAK or abort the install and leave it as is.
  1530. //
  1531. if (bSilent)
  1532. {
  1533. return FALSE;
  1534. }
  1535. else
  1536. {
  1537. MYVERIFY(0 != LoadString(hInstance, IDS_INSTCM_WITH_OLD_CMAK, szMsg, 2*MAX_PATH));
  1538. if (IDNO == MessageBox(NULL, szMsg, szServiceName, MB_YESNO | MB_DEFBUTTON2 | MB_ICONEXCLAMATION))
  1539. {
  1540. return FALSE;
  1541. }
  1542. }
  1543. }
  1544. *pbInstallCm = TRUE;
  1545. bReturn = TRUE;
  1546. }
  1547. }
  1548. }
  1549. }
  1550. else
  1551. {
  1552. if (bCMRequired)
  1553. {
  1554. //
  1555. // This is an error because we need CM bits but don't have any on
  1556. // the machine or in the cab (or its Whistler and we won't install them).
  1557. //
  1558. if (!bSilent)
  1559. {
  1560. MYVERIFY(0 != LoadString(hInstance, IDS_CM_NOTPRESENT, szMsg, MAX_PATH));
  1561. MessageBox(NULL, szMsg, szServiceName, MB_OK);
  1562. }
  1563. return FALSE;
  1564. }
  1565. else
  1566. {
  1567. MYDBGASSERT(FALSE == plat.IsNT5()); // we shouldn't be in this state on Win2k but it is probably
  1568. // better for the user if we install.
  1569. *pbInstallCm = TRUE;
  1570. bReturn = TRUE;
  1571. }
  1572. }
  1573. if (!bNoSupportFiles)
  1574. {
  1575. if (!CheckIeDllRequirements(&plat))
  1576. {
  1577. if (!bSilent)
  1578. {
  1579. MYVERIFY(0 != LoadString(hInstance, IDS_NO_SUPPORTFILES, szMsg, MAX_PATH));
  1580. MessageBox(NULL, szMsg, szServiceName, MB_OK);
  1581. }
  1582. return FALSE;
  1583. }
  1584. }
  1585. return bReturn;
  1586. }
  1587. //+----------------------------------------------------------------------------
  1588. //
  1589. // Function: GetInstallOptions
  1590. //
  1591. // Synopsis: This function decides if the profile should be installed for all
  1592. // users or the current user only, as well as whether the user prefers
  1593. // a desktop icon, a start menu link, both, or neither.
  1594. //
  1595. // Arguments: OUT BOOL* pbInstallForAllUsers - should the profile be installed for all users
  1596. // OUT BOOL* pbCreateDesktopIcon - should a desktop icon be created (if NT5)
  1597. // IN BOOL bCM10Upgrade - is this profile upgrading an older same name profile
  1598. // IN BOOL bNoNT5Shortcut - whether the user specified a switch saying they didn't want an NT5 Shortcut
  1599. // IN BOOL bSilentSingleUser - whether the user specified a switch saying they wanted a silent Single User install
  1600. // IN BOOL bSilentAllUser - whether the user specified a switch saying they wanted a silent ALL User install
  1601. //
  1602. // Returns: TRUE if the install should continue, FALSE otherwise
  1603. //
  1604. // History: quintinb Created 11/1/98
  1605. //
  1606. //+----------------------------------------------------------------------------
  1607. BOOL GetInstallOptions(HINSTANCE hInstance, BOOL* pbInstallForAllUsers,
  1608. BOOL* pbCreateDesktopIcon, BOOL bCM10Upgrade, BOOL bNoNT5Shortcut,
  1609. BOOL bSingleUser, BOOL bSilent, LPTSTR pszServiceName)
  1610. {
  1611. //
  1612. // We will only allow NT5 users who are administrators to have a choice of how
  1613. // the profile is installed. If the user is on a legacy platform then the profile
  1614. // will be installed for all users just as before. If the profile is installed by
  1615. // an NT5 user that is not an admin, it will be installed just for them. If they
  1616. // are an admin then they can choose if they want the profile available to all users
  1617. // or just for themselves. If we are on NT5 we also allow the user to choose if
  1618. // they want a Desktop Shortcut or not.
  1619. //
  1620. INT_PTR iUiReturn;
  1621. CPlatform plat;
  1622. if (plat.IsWin9x() || plat.IsNT4())
  1623. {
  1624. //
  1625. // Legacy install, force to all users (ignore SingleUser flag because not supported).
  1626. //
  1627. *pbInstallForAllUsers = TRUE;
  1628. }
  1629. else
  1630. {
  1631. int iDialogID;
  1632. if (bSilent)
  1633. {
  1634. *pbCreateDesktopIcon = !bNoNT5Shortcut;
  1635. if (IsAdmin() && !bSingleUser)
  1636. {
  1637. *pbInstallForAllUsers = TRUE;
  1638. }
  1639. else
  1640. {
  1641. *pbInstallForAllUsers = FALSE;
  1642. }
  1643. }
  1644. else
  1645. {
  1646. if (IsAdmin())
  1647. {
  1648. //
  1649. // The user is a local admin, we need to prompt to see if they want to install
  1650. // the profile for themselves or for all users. However, if we are doing a
  1651. // same name upgrade, then we always do an all users install and don't give the
  1652. // admin any choice.
  1653. //
  1654. if (bCM10Upgrade)
  1655. {
  1656. iDialogID = IDD_NOCHOICEUI;
  1657. }
  1658. else
  1659. {
  1660. iDialogID = IDD_ADMINUI;
  1661. }
  1662. }
  1663. else
  1664. {
  1665. //
  1666. // Just a normal user, but we still need to prompt for whether they want
  1667. // a desktop shortcut
  1668. //
  1669. if (bCM10Upgrade)
  1670. {
  1671. CMASSERTMSG(FALSE, TEXT("Non-Admin NT5 made it to UI choice section. Check CM 1.0 upgrade code."));
  1672. return FALSE;
  1673. }
  1674. else
  1675. {
  1676. iDialogID = IDD_NOCHOICEUI;
  1677. }
  1678. }
  1679. InitDialogStruct DialogArgs;
  1680. DialogArgs.pszTitle = pszServiceName;
  1681. DialogArgs.bNoDesktopIcon = bNoNT5Shortcut;
  1682. DialogArgs.bSingleUser = bSingleUser;
  1683. iUiReturn = DialogBoxParam(hInstance, MAKEINTRESOURCE(iDialogID), NULL,
  1684. (DLGPROC)ProcessPreferencesUI, (LPARAM)&DialogArgs);
  1685. if (-1 == iUiReturn)
  1686. {
  1687. // then we had an error or the user hit cancel. Either way bail.
  1688. return FALSE;
  1689. }
  1690. else
  1691. {
  1692. *pbInstallForAllUsers = (BOOL)(iUiReturn & ALLUSERS) || bCM10Upgrade;
  1693. *pbCreateDesktopIcon = (BOOL)(iUiReturn & CREATEDESKTOPICON);
  1694. }
  1695. }
  1696. }
  1697. return TRUE;
  1698. }
  1699. BOOL VerifyProfileOverWriteIfExists(HINSTANCE hInstance, LPCTSTR pszCmsFile, LPCTSTR pszServiceName,
  1700. LPCTSTR pszShortServiceName, LPTSTR pszOldInfPath, BOOL bSilent)
  1701. {
  1702. TCHAR szTmpServiceName [MAX_PATH+1] = TEXT("");
  1703. TCHAR szDisplayMsg[3*MAX_PATH+1] = TEXT("");
  1704. TCHAR szTemp [2*MAX_PATH+1] = TEXT("");
  1705. int iRet;
  1706. if (FileExists(pszCmsFile))
  1707. {
  1708. //
  1709. // If the file exists then we want to make sure that the service name is the same.
  1710. // If the Long Service Names are the same then we have a re-install.
  1711. // If they aren't the same then we need to prompt the user and find out whether to
  1712. // abandon the install or continue and overwrite it.
  1713. //
  1714. MYVERIFY(0 != GetPrivateProfileString(c_pszCmSection, c_pszCmEntryServiceName,
  1715. TEXT(""), szTmpServiceName, CELEMS(szTmpServiceName), pszCmsFile));
  1716. if (0 != lstrcmp(szTmpServiceName, pszServiceName))
  1717. {
  1718. //
  1719. // If the install is silent, we will assume they know what they are doing
  1720. // and we will overwrite. Otherwise prompt the user to see what they want
  1721. // us to do.
  1722. //
  1723. if (!bSilent)
  1724. {
  1725. MYVERIFY(0 != LoadString(hInstance, IDS_SAME_SS_DIFF_LS, szTemp, 2*MAX_PATH));
  1726. MYVERIFY(CELEMS(szDisplayMsg) > (UINT)wsprintf(szDisplayMsg, szTemp, pszServiceName,
  1727. szTmpServiceName, pszShortServiceName));
  1728. MessageBox(NULL, szDisplayMsg, pszServiceName, MB_OK | MB_TOPMOST | MB_SYSTEMMODAL);
  1729. }
  1730. return FALSE;
  1731. }
  1732. }
  1733. return TRUE;
  1734. }
  1735. //+----------------------------------------------------------------------------
  1736. //
  1737. // Function: PresharedKeyPINDlgProc
  1738. //
  1739. // Synopsis: This function obtains the PIN to be used for the Pre-Shared key
  1740. //
  1741. // History: SumitC 29-Mar-2001 Created
  1742. //
  1743. //+----------------------------------------------------------------------------
  1744. BOOL APIENTRY PresharedKeyPINDlgProc(
  1745. HWND hDlg,
  1746. UINT message,
  1747. WPARAM wParam,
  1748. LPARAM lParam)
  1749. {
  1750. static PresharedKeyPINStruct * pPSKArgs;
  1751. switch (message)
  1752. {
  1753. case WM_INITDIALOG:
  1754. pPSKArgs = (PresharedKeyPINStruct*)lParam;
  1755. break;
  1756. case WM_COMMAND:
  1757. switch (LOWORD(wParam))
  1758. {
  1759. case IDOK:
  1760. MYDBGASSERT(pPSKArgs);
  1761. if (pPSKArgs && pPSKArgs->szPIN)
  1762. {
  1763. GetDlgItemText(hDlg, IDC_PSK_PIN, pPSKArgs->szPIN, c_dwMaxPresharedKeyPIN);
  1764. }
  1765. MYVERIFY(FALSE != EndDialog(hDlg, 1));
  1766. return TRUE;
  1767. case IDCANCEL:
  1768. MYVERIFY(FALSE != EndDialog(hDlg, -1));
  1769. return TRUE;
  1770. default:
  1771. break;
  1772. }
  1773. break;
  1774. case WM_CLOSE:
  1775. MYVERIFY(FALSE != EndDialog(hDlg, -1));
  1776. return TRUE;
  1777. default:
  1778. return FALSE;
  1779. }
  1780. return FALSE;
  1781. }
  1782. //+----------------------------------------------------------------------------
  1783. //
  1784. // Function: GetPINforPresharedKey
  1785. //
  1786. // Synopsis: Asks the user for a PIN (to be used to decrypt the pre-shared key)
  1787. //
  1788. // Arguments: [hInstance] - used for bringing up dialogs
  1789. // [ppszPIN] - ptr to where to put Pre-Shared Key PIN
  1790. //
  1791. // Returns: LRESULT (ERROR_SUCCESS if we got a PIN,
  1792. // ERROR_INVALID_DATA if user cancelled out of PIN dialog,
  1793. // ERROR_INVALID_PARAMETER if params are bad (this is a coding issue)
  1794. //
  1795. // History: 3-Apr-2001 SumitC Created
  1796. //
  1797. //-----------------------------------------------------------------------------
  1798. LRESULT GetPINforPresharedKey(HINSTANCE hInstance, LPTSTR * ppszPIN)
  1799. {
  1800. LRESULT lRet = ERROR_SUCCESS;
  1801. MYDBGASSERT(hInstance);
  1802. MYDBGASSERT(ppszPIN);
  1803. if (NULL == hInstance || NULL == ppszPIN)
  1804. {
  1805. return ERROR_INVALID_PARAMETER;
  1806. }
  1807. *ppszPIN = NULL;
  1808. //
  1809. // Get the PIN
  1810. //
  1811. PresharedKeyPINStruct PresharedKeyPINArgs = {0};
  1812. INT_PTR iUiReturn = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_PRESHAREDKEY_PIN), NULL,
  1813. (DLGPROC)PresharedKeyPINDlgProc, (LPARAM)&PresharedKeyPINArgs);
  1814. if (-1 == iUiReturn)
  1815. {
  1816. lRet = ERROR_INVALID_DATA; // caller maps to appropriate error message.
  1817. }
  1818. else
  1819. {
  1820. DWORD dwLen = lstrlen(PresharedKeyPINArgs.szPIN);
  1821. if (0 == dwLen)
  1822. {
  1823. lRet = ERROR_INVALID_DATA; // caller maps to appropriate error message.
  1824. }
  1825. else
  1826. {
  1827. *ppszPIN = (LPTSTR) CmMalloc((dwLen + 1) * sizeof(TCHAR));
  1828. if (*ppszPIN)
  1829. {
  1830. lstrcpy(*ppszPIN, PresharedKeyPINArgs.szPIN);
  1831. }
  1832. }
  1833. }
  1834. return lRet;
  1835. }
  1836. //+----------------------------------------------------------------------------
  1837. //
  1838. // Function: DecryptPresharedKeyUsingPIN
  1839. //
  1840. // Synopsis: Given an encoded preshared key and a PIN to be used for decoding,
  1841. // performs the decoding job.
  1842. //
  1843. // Arguments: [pszEncodedPresharedKey] - encoded Preshared key
  1844. // [pszPreSharedKeyPIN] - the PIN
  1845. // [ppszPreSharedKey] - ptr to buffer to place Pre-Shared Key
  1846. //
  1847. // Returns: LRESULT (ERROR_SUCCESS successfully decoded
  1848. // ERROR_INVALID_PARAMETER if params are bad (this is a coding issue)
  1849. // other errors as encountered while calling crypto APIs
  1850. //
  1851. // History: 3-Apr-2001 SumitC Created
  1852. //
  1853. //-----------------------------------------------------------------------------
  1854. LRESULT DecryptPresharedKeyUsingPIN(LPCTSTR pszEncodedPresharedKey,
  1855. LPCTSTR pszPresharedKeyPIN,
  1856. LPTSTR * ppszPresharedKey)
  1857. {
  1858. LRESULT lRet = ERROR_BADKEY;
  1859. if (lstrlen(pszPresharedKeyPIN) < c_dwMinPresharedKeyPIN)
  1860. {
  1861. CMTRACE(TEXT("DecryptPresharedKeyUsingPIN - PIN is too short"));
  1862. return lRet;
  1863. }
  1864. if (lstrlen(pszPresharedKeyPIN) > c_dwMaxPresharedKeyPIN)
  1865. {
  1866. CMTRACE(TEXT("DecryptPresharedKeyUsingPIN - PIN is too long"));
  1867. return lRet;
  1868. }
  1869. //
  1870. // Init Cmsecure
  1871. //
  1872. InitSecure(FALSE); // use secure, not fast encryption
  1873. //
  1874. // decrypt to get Preshared key
  1875. //
  1876. if (pszEncodedPresharedKey && pszPresharedKeyPIN)
  1877. {
  1878. DWORD dwLen = 0;
  1879. if (FALSE == DecryptString((LPBYTE)pszEncodedPresharedKey,
  1880. lstrlen(pszEncodedPresharedKey) * sizeof(TCHAR),
  1881. (LPSTR)pszPresharedKeyPIN,
  1882. (LPBYTE *)ppszPresharedKey,
  1883. &dwLen,
  1884. (PFN_CMSECUREALLOC)CmMalloc,
  1885. (PFN_CMSECUREFREE)CmFree))
  1886. {
  1887. CMTRACE1(TEXT("DecryptPresharedKeyUsingPIN - DecryptString failed with %d"), GetLastError());
  1888. lRet = ERROR_BADKEY;
  1889. }
  1890. else
  1891. {
  1892. lRet = ERROR_SUCCESS;
  1893. CMASSERTMSG(dwLen, TEXT("DecryptString succeeded, but pre-shared key retrieved was 0 bytes?"));
  1894. }
  1895. }
  1896. //
  1897. // Deinit cmsecure
  1898. //
  1899. DeInitSecure();
  1900. return lRet;
  1901. }
  1902. //+----------------------------------------------------------------------------
  1903. //
  1904. // Function: SetThisConnectionAsDefault
  1905. //
  1906. // Synopsis: This function loads inetcfg.dll and calls the InetSetAutodial
  1907. // entry on the given service name. Thus this function sets the
  1908. // given servicename as the IE default connection.
  1909. //
  1910. // Arguments: LPCSTR pszServiceName - Long service name of the connection to set
  1911. //
  1912. // Returns: BOOL - TRUE if successful
  1913. //
  1914. // History: quintinb Created 03/04/00
  1915. //
  1916. //+----------------------------------------------------------------------------
  1917. BOOL SetThisConnectionAsDefault(LPSTR pszServiceName)
  1918. {
  1919. BOOL bReturn = FALSE;
  1920. typedef HRESULT (WINAPI *pfnInetSetAutodialProc)(BOOL, LPSTR);
  1921. if (pszServiceName && (TEXT('\0') != pszServiceName[0]))
  1922. {
  1923. CDynamicLibrary CnetCfg;
  1924. if (CnetCfg.Load(TEXT("inetcfg.dll")))
  1925. {
  1926. pfnInetSetAutodialProc pfnInetSetAutodial = (pfnInetSetAutodialProc)CnetCfg.GetProcAddress("InetSetAutodial");
  1927. if (pfnInetSetAutodial)
  1928. {
  1929. HRESULT hr = pfnInetSetAutodial(TRUE, pszServiceName); // TRUE == fEnable
  1930. bReturn = SUCCEEDED(hr);
  1931. }
  1932. }
  1933. }
  1934. return bReturn;
  1935. }
  1936. //+----------------------------------------------------------------------------
  1937. //
  1938. // Function: GetPreSharedKey
  1939. //
  1940. // Synopsis: This function does all the grunt work of getting a PreSharedKey
  1941. // out of the profile and decrypting it if one exists.
  1942. //
  1943. // Arguments: LPCSTR pszCmpFile - full path to the cmp file
  1944. //
  1945. // Returns: HRESULT -- S_OK on success
  1946. // S_FALSE if the profile doesn't contain a PSK
  1947. // E_XXX otherwise
  1948. //
  1949. // History: quintinb Created 09/14/01
  1950. //
  1951. //+----------------------------------------------------------------------------
  1952. HRESULT GetPreSharedKey(HINSTANCE hInstance, LPCTSTR pszCmpFile, LPCTSTR pszServiceName, LPTSTR* ppszPreSharedKey, BOOL bSilent)
  1953. {
  1954. //
  1955. // Check input parameters
  1956. //
  1957. if ((NULL == pszCmpFile) || (TEXT('\0') == pszCmpFile[0]) || (NULL == ppszPreSharedKey) || (NULL == pszServiceName) || (TEXT('\0') == pszServiceName[0]))
  1958. {
  1959. CMASSERTMSG(FALSE, TEXT("GetPreSharedKey -- Invalid parameter passed."));
  1960. return E_INVALIDARG;
  1961. }
  1962. HRESULT hrReturn = S_OK;
  1963. TCHAR szTemp[2*MAX_PATH+1];
  1964. CPlatform plat;
  1965. //
  1966. // Check to see if we have a Pre-shared Key
  1967. //
  1968. if (FileExists(pszCmpFile))
  1969. {
  1970. LPTSTR pszPresharedKey = GetPrivateProfileStringWithAlloc(c_pszCmSection, c_pszCmEntryPresharedKey, TEXT(""), pszCmpFile);
  1971. if (pszPresharedKey && (TEXT('\0') != pszPresharedKey[0]))
  1972. {
  1973. //
  1974. // Okay, we potentially have a PSK. If we are on Win2k or a downlevel system without the SafeNet client,
  1975. // then we need to warn the user that their connection might not work and ask them if they wish to continue
  1976. // anyway.
  1977. //
  1978. UINT uMessageId = 0;
  1979. if (plat.IsNT5()) // NT5 only
  1980. {
  1981. uMessageId = IDS_PSK_NEEDS_XP;
  1982. }
  1983. else if (plat.IsNT4() || plat.IsWin9x()) // downlevel without SafeNet
  1984. {
  1985. SafeNetLinkageStruct SnLinkage = {0};
  1986. if (!IsSafeNetClientAvailable() || !LinkToSafeNet(&SnLinkage)) // UnLinkFromSafeNet needs to be called if we hit this
  1987. {
  1988. uMessageId = IDS_PSK_NEEDS_SAFENET;
  1989. }
  1990. UnLinkFromSafeNet(&SnLinkage);
  1991. }
  1992. // else it is WinXP+ or downlevel with SafeNet and everything should work just nicely...
  1993. if (uMessageId)
  1994. {
  1995. //
  1996. // Now show the user the error message unless we are in silent mode. Either way we aren't going to do anything
  1997. // more with the PSK string since the user's OS cannot handle pre-shared keys.
  1998. //
  1999. int iMessageReturn = IDNO;
  2000. if (!bSilent && LoadString(hInstance, uMessageId, szTemp, sizeof(szTemp)))
  2001. {
  2002. iMessageReturn = MessageBox(NULL, szTemp, pszServiceName, MB_YESNO | MB_ICONWARNING);
  2003. }
  2004. if (IDYES == iMessageReturn)
  2005. {
  2006. //
  2007. // Okay, we are continuing but we cannot do anything with the PSK, so set the return code to S_FALSE.
  2008. //
  2009. hrReturn = S_FALSE;
  2010. }
  2011. else
  2012. {
  2013. //
  2014. // The user decided to bail or we are in silent mode and needed to show a warning.
  2015. //
  2016. hrReturn = E_ABORT;
  2017. }
  2018. //
  2019. // We cannot do anything with the PSK so let's free it. We will erase it from the cmp below.
  2020. //
  2021. CmFree(pszPresharedKey);
  2022. pszPresharedKey = NULL;
  2023. }
  2024. else
  2025. {
  2026. //
  2027. // Okay, we have a PSK and it needs further processing such as decryption and/or handing off to RAS/SafeNet.
  2028. //
  2029. CMTRACE(TEXT("Got a pre-shared key"));
  2030. BOOL bEncrypted = (BOOL) GetPrivateProfileInt(c_pszCmSection, c_pszCmEntryKeyIsEncrypted, FALSE, pszCmpFile);
  2031. if (bEncrypted)
  2032. {
  2033. CMTRACE(TEXT("Pre-shared key is encrypted"));
  2034. if (bSilent)
  2035. {
  2036. //
  2037. // If we are in silent mode, then we cannot exactly ask the user for a PIN. Thus we will have to abort.
  2038. //
  2039. CmFree(pszPresharedKey);
  2040. hrReturn = E_ABORT;
  2041. }
  2042. else
  2043. {
  2044. LPTSTR pszPresharedKeyPIN = NULL;
  2045. LRESULT lRet = GetPINforPresharedKey(hInstance, &pszPresharedKeyPIN);
  2046. if ((ERROR_SUCCESS == lRet) && pszPresharedKeyPIN)
  2047. {
  2048. //
  2049. // The Pre-shared key is encoded
  2050. //
  2051. LPTSTR pszPresharedKeyDecoded = NULL;
  2052. lRet = DecryptPresharedKeyUsingPIN(pszPresharedKey, pszPresharedKeyPIN, &pszPresharedKeyDecoded);
  2053. if (ERROR_SUCCESS == lRet)
  2054. {
  2055. *ppszPreSharedKey = pszPresharedKeyDecoded;
  2056. }
  2057. else
  2058. {
  2059. *ppszPreSharedKey = NULL;
  2060. lRet = ERROR_BADKEY;
  2061. }
  2062. }
  2063. CmFree(pszPresharedKey);
  2064. CmFree(pszPresharedKeyPIN);
  2065. if (ERROR_SUCCESS != lRet)
  2066. {
  2067. switch (lRet)
  2068. {
  2069. case ERROR_INVALID_DATA:
  2070. MYVERIFY(0 != LoadString(hInstance, IDS_PSK_GOTTA_HAVE_IT, szTemp, CELEMS(szTemp)));
  2071. break;
  2072. case ERROR_BADKEY:
  2073. MYVERIFY(0 != LoadString(hInstance, IDS_PSK_INCORRECT_PIN, szTemp, CELEMS(szTemp)));
  2074. break;
  2075. default:
  2076. MYVERIFY(0 != LoadString(hInstance, IDS_UNEXPECTEDERR, szTemp, CELEMS(szTemp)));
  2077. MYDBGASSERT(0);
  2078. break;
  2079. }
  2080. MessageBox(NULL, szTemp, pszServiceName, MB_OK | MB_ICONEXCLAMATION);
  2081. hrReturn = E_ACCESSDENIED;
  2082. }
  2083. }
  2084. }
  2085. else
  2086. {
  2087. //
  2088. // Nothing to decode, just save the PSK buffer
  2089. //
  2090. *ppszPreSharedKey = pszPresharedKey;
  2091. }
  2092. //
  2093. // Write to the cmp file that we need to delete this PSK on uninstall. If we never get fully
  2094. // installed then the cmp file gets deleted anyway so no big deal. Note that we do this here
  2095. // so that it is in the cmp file before we copy it to the profile directory.
  2096. //
  2097. if (SUCCEEDED(hrReturn) && (plat.IsNT4() || plat.IsWin9x()))
  2098. {
  2099. WritePrivateProfileString(c_pszCmSection, c_pszDeletePskOnUninstall, TEXT("1"), pszCmpFile);
  2100. }
  2101. }
  2102. //
  2103. // Erase the PSK from the cmp file
  2104. //
  2105. WritePrivateProfileString(c_pszCmSection, c_pszCmEntryPresharedKey, NULL, pszCmpFile);
  2106. WritePrivateProfileString(c_pszCmSection, c_pszCmEntryKeyIsEncrypted, NULL, pszCmpFile);
  2107. }
  2108. else
  2109. {
  2110. CmFree(pszPresharedKey);
  2111. hrReturn = S_FALSE;
  2112. }
  2113. }
  2114. return hrReturn;
  2115. }
  2116. //+----------------------------------------------------------------------------
  2117. //
  2118. // Function: InstallInf
  2119. //
  2120. // Synopsis: This is the driver code for installing a CM profile.
  2121. //
  2122. // Arguments: HINSTANCE hInstance - Instance handle for resources
  2123. // LPCTSTR szInfFile - INF file to install
  2124. // BOOL bNoSupportFiles - forces browser files not to be checked for
  2125. // BOOL bNoLegacyIcon - Don't install with a legacy Icon
  2126. // BOOL bNoNT5Shortcut - Don't give the user a NT5 Desktop Shortcut
  2127. // BOOL bSilent - Install the profile silently
  2128. // BOOL bSingleUser - Install the profile for the current user only
  2129. // Note that single user is the default now even for
  2130. // Admins. Non-Admins always get single user installs.
  2131. // BOOL bSetAsDefault - set as the default connection once installed
  2132. // CNamedMutex* pCmstpMutex - pointer to the cmstp mutex object so
  2133. // that it can be released once the profile is launched
  2134. //
  2135. // Returns: HRESULT - standard COM error codes
  2136. //
  2137. // History: quintinb Created 7/14/98
  2138. // quintinb added support for new switches (252872) 11/20/98
  2139. //
  2140. //+----------------------------------------------------------------------------
  2141. HRESULT InstallInf(HINSTANCE hInstance, LPCTSTR szInfFile, BOOL bNoSupportFiles,
  2142. BOOL bNoLegacyIcon, BOOL bNoNT5Shortcut, BOOL bSilent,
  2143. BOOL bSingleUser, BOOL bSetAsDefault, CNamedMutex* pCmstpMutex)
  2144. {
  2145. CPlatform plat;
  2146. BOOL bMigrateExistingProfiles;
  2147. BOOL bInstallCm;
  2148. BOOL bMustReboot = FALSE;
  2149. BOOL bCM10Upgrade = FALSE;
  2150. HRESULT hrReturn = S_OK;
  2151. HRESULT hrTemp = S_OK;
  2152. BOOL bInstallForAllUsers;
  2153. BOOL bCreateDesktopIcon = FALSE;
  2154. HKEY hKey = NULL;
  2155. DWORD dwSize;
  2156. DWORD dwType;
  2157. TCHAR szInstallDir[MAX_PATH+1];
  2158. TCHAR szTemp[2*MAX_PATH+1];
  2159. TCHAR szCmsFile[MAX_PATH+1];
  2160. TCHAR szOldInfPath[MAX_PATH+1];
  2161. TCHAR szServiceName[MAX_PATH+1];
  2162. TCHAR szShortServiceName[MAX_PATH+1];
  2163. TCHAR szTitle[MAX_PATH+1];
  2164. LPTSTR pszPhonebook = NULL;
  2165. LPTSTR pszCmpFile = NULL;
  2166. LPTSTR pszPresharedKey = NULL;
  2167. //CMASSERTMSG(FALSE, TEXT("Attach the Debugger now!"));
  2168. MYDBGASSERT((szInfFile) && (TEXT('\0') != szInfFile[0]));
  2169. CFileNameParts InfParts(szInfFile);
  2170. wsprintf(g_szProfileSourceDir, TEXT("%s%s"), InfParts.m_Drive, InfParts.m_Dir);
  2171. MYVERIFY(0 != LoadString(hInstance, IDS_CMSTP_TITLE, szTitle, CELEMS(szTitle)));
  2172. MYDBGASSERT(TEXT('\0') != szTitle[0]);
  2173. //
  2174. // Get the ServiceName and ShortServicename from the inf file
  2175. //
  2176. MYVERIFY(0 != GetPrivateProfileString(c_pszInfSectionStrings, c_pszCmEntryServiceName,
  2177. TEXT(""), szServiceName, CELEMS(szServiceName), szInfFile));
  2178. MYVERIFY(0 != GetPrivateProfileString(c_pszInfSectionStrings, c_pszShortSvcName,
  2179. TEXT(""), szShortServiceName, CELEMS(szShortServiceName), szInfFile));
  2180. if ((TEXT('\0') == szServiceName[0]) || (TEXT('\0') == szShortServiceName[0]))
  2181. {
  2182. CMASSERTMSG(FALSE, TEXT("Either the ServiceName or the ShortServiceName are empty, exiting."));
  2183. hrReturn = E_FAIL;
  2184. goto exit;
  2185. }
  2186. //
  2187. // If this is NT5, check the New Connection Wizard Policy to see if the user is allowed to
  2188. // create new connections. If not, then don't let them install.
  2189. //
  2190. if (plat.IsAtLeastNT5())
  2191. {
  2192. LPTSTR c_pszNewPolicy = TEXT("NC_NewConnectionWizard");
  2193. LPTSTR c_pszConnectionsPoliciesKey = TEXT("Software\\Policies\\Microsoft\\Windows\\Network Connections");
  2194. //
  2195. // Administrators and all Authenticated users have access to install profiles
  2196. // by default. Non-Authenticated users don't have access to install profiles
  2197. // because they don't have permission to start Rasman. Thus, even if we
  2198. // allowed them to try to install, it would fail when we couldn't create a
  2199. // connectoid for the profile.
  2200. //
  2201. DWORD dwAllowedToInstall = IsAuthenticatedUser() || IsAdmin();
  2202. //
  2203. // Now we need to check the policy registry key to see if someone has overriden
  2204. // the default behavior. If so, then we will honor it by setting dwAllowedToInstall
  2205. // to the value of the policy key. Note that we even check the registry key for
  2206. // authenticated users (an Admin could enable installation for all users, but users
  2207. // that weren't Authenticated, namely guests, wouldn't be able to The default is to allow Users, Power Users (who are users), and Admins to install
  2208. // connections. However the policy may be setup so that they cannot. Lets assume they
  2209. // can and then check the regkey.
  2210. //
  2211. if (dwAllowedToInstall)
  2212. {
  2213. LONG lResult = RegOpenKeyEx(HKEY_CURRENT_USER, c_pszConnectionsPoliciesKey,
  2214. 0, KEY_READ, &hKey);
  2215. if (ERROR_SUCCESS == lResult)
  2216. {
  2217. dwSize = sizeof(dwAllowedToInstall);
  2218. lResult = RegQueryValueEx(hKey, c_pszNewPolicy, NULL,
  2219. NULL, (LPBYTE)&dwAllowedToInstall, &dwSize);
  2220. RegCloseKey(hKey);
  2221. }
  2222. }
  2223. if (!dwAllowedToInstall)
  2224. {
  2225. //
  2226. // The user isn't allowed to create new connections, thus they aren't allowed to install
  2227. // CM connections. Throw an error message about permissions and exit.
  2228. //
  2229. MYVERIFY(0 != LoadString(hInstance, IDS_INSTALL_NOT_ALLOWED, szTemp, CELEMS(szTemp)));
  2230. MessageBox(NULL, szTemp, szServiceName, MB_OK);
  2231. hrReturn = E_ACCESSDENIED;
  2232. goto exit;
  2233. }
  2234. }
  2235. if (!CheckCmAndIeRequirements(hInstance, &bInstallCm, &bMigrateExistingProfiles,
  2236. szInfFile, bNoSupportFiles, szServiceName, bSilent))
  2237. {
  2238. hrReturn = E_FAIL;
  2239. goto exit;
  2240. }
  2241. //
  2242. // Check to see if we have a same name upgrade
  2243. //
  2244. bCM10Upgrade = NeedCM10Upgrade(hInstance, szServiceName, szShortServiceName,
  2245. szOldInfPath, bSilent, &plat);
  2246. if (-1 == bCM10Upgrade)
  2247. {
  2248. //
  2249. // if NeedCM10Upgrade returned -1 then either an error occurred or
  2250. // the user decided not to upgrade. Either way, bail.
  2251. //
  2252. hrReturn = S_FALSE;
  2253. goto exit;
  2254. }
  2255. //
  2256. // Check to see if a Pre-shared Key is present and if a PIN is required
  2257. //
  2258. pszCmpFile = szTemp; // re-use szTemp to save stack space and not get into trouble on Win9x
  2259. MYVERIFY(CELEMS(szTemp) > (UINT)wsprintf(pszCmpFile, TEXT("%s%s.cmp"),
  2260. g_szProfileSourceDir, szShortServiceName));
  2261. hrReturn = GetPreSharedKey(hInstance, pszCmpFile, szServiceName, &pszPresharedKey, bSilent);
  2262. if (FAILED(hrReturn))
  2263. {
  2264. goto exit;
  2265. }
  2266. //
  2267. // Throw the UI to get user install options, unless we are in silent mode
  2268. //
  2269. if (!GetInstallOptions(hInstance, &bInstallForAllUsers, &bCreateDesktopIcon,
  2270. bCM10Upgrade, bNoNT5Shortcut, bSingleUser, bSilent, szServiceName))
  2271. {
  2272. hrReturn = S_FALSE;
  2273. goto exit;
  2274. }
  2275. //
  2276. // Get the installation path
  2277. //
  2278. ZeroMemory(szInstallDir, sizeof(szInstallDir));
  2279. if (bInstallForAllUsers)
  2280. {
  2281. //
  2282. // Install for All Users
  2283. //
  2284. if (!GetAllUsersCmDir(szInstallDir, hInstance))
  2285. {
  2286. hrReturn = E_FAIL;
  2287. goto exit;
  2288. }
  2289. }
  2290. else
  2291. {
  2292. //
  2293. // Install only for the current user
  2294. //
  2295. GetPrivateCmUserDir(szInstallDir, hInstance); //lint !e534
  2296. if (TEXT('\0') == szInstallDir[0])
  2297. {
  2298. hrReturn = E_FAIL;
  2299. goto exit;
  2300. }
  2301. }
  2302. MYVERIFY(CELEMS(szCmsFile) > (UINT)wsprintf(szCmsFile, TEXT("%s\\%s\\%s.cms"),
  2303. szInstallDir, szShortServiceName, szShortServiceName));
  2304. //
  2305. // Check for two profiles with the same Short Service Name and different Long Service
  2306. // Names
  2307. //
  2308. if (!VerifyProfileOverWriteIfExists(hInstance, szCmsFile,
  2309. szServiceName, szShortServiceName, szOldInfPath, bSilent))
  2310. {
  2311. hrReturn = S_FALSE;
  2312. goto exit;
  2313. }
  2314. // Now Migrate users old cm profiles (to have full paths to their CMP files in the
  2315. // desktop GUID) if necessary
  2316. //
  2317. if (bMigrateExistingProfiles)
  2318. {
  2319. //
  2320. // Ignore the return here for now. Not much we can do about it at this stage.
  2321. // Should we give them an error?
  2322. //
  2323. MYVERIFY(SUCCEEDED(MigrateOldCmProfilesForProfileInstall(hInstance, g_szProfileSourceDir)));
  2324. }
  2325. if (bCM10Upgrade)
  2326. {
  2327. //
  2328. // Uninstall the current profile so that we can install the newer version. Note
  2329. // that we don't want to use the uninstall string because it might call for
  2330. // cmstp.exe which is already running. Thus uninstall by calling UninstallProfile
  2331. // directly. Note that we do not delete the credentials on a same name upgrade
  2332. // profile uninstall.
  2333. //
  2334. if (szOldInfPath[0])
  2335. {
  2336. RemoveShowIconFromRunPostSetupCommands(szOldInfPath);
  2337. MYVERIFY(SUCCEEDED(UninstallProfile(hInstance, szOldInfPath, FALSE))); // bCleanUpCreds == FALSE
  2338. }
  2339. }
  2340. else
  2341. {
  2342. //
  2343. // We need to check if we are installing over another profile of the same name.
  2344. // If so, then we want to recover the cmp data unless this is a CM 1.0 upgrade
  2345. // in which case we have already done this as part of that upgrade code.
  2346. //
  2347. if (-1 == MigrateCmpData(hInstance, bInstallForAllUsers, szServiceName, szShortServiceName, bSilent))
  2348. {
  2349. hrReturn = S_FALSE;
  2350. goto exit;
  2351. }
  2352. }
  2353. //
  2354. // In order to keep MSN's online setup working we need to keep the all user install
  2355. // registry key (used to communicate the path to the inf) in the same place that it was
  2356. // for the Win98 SE/Beta3 release. The Single user reg key location had to be moved to
  2357. // allow plain old users to install profiles.
  2358. //
  2359. HKEY hBaseKey;
  2360. LPTSTR pszRegInfCommKeyPath;
  2361. if (bInstallForAllUsers)
  2362. {
  2363. hBaseKey = HKEY_LOCAL_MACHINE;
  2364. pszRegInfCommKeyPath = (LPTSTR)c_pszRegCmAppPaths;
  2365. }
  2366. else
  2367. {
  2368. hBaseKey = HKEY_CURRENT_USER;
  2369. pszRegInfCommKeyPath = (LPTSTR)c_pszRegCmRoot;
  2370. }
  2371. //
  2372. // Now create the install dir and the reg key to communicate this info to the inf file.
  2373. //
  2374. if (TEXT('\0') != szInstallDir[0])
  2375. {
  2376. //
  2377. // Create the full path to the installation directory.
  2378. //
  2379. MYVERIFY(FALSE != CreateLayerDirectory(szInstallDir));
  2380. //
  2381. // Make sure the CM directory is writable by all users so that phonebook updates can take place.
  2382. //
  2383. if (bInstallForAllUsers && plat.IsAtLeastNT5())
  2384. {
  2385. MYVERIFY(AllowAccessToWorld(szInstallDir));
  2386. }
  2387. //
  2388. // Create the Profile subdirectory too, that way we avoid profile
  2389. // install problems on Win98 -- NTRAID 376878
  2390. //
  2391. MYVERIFY(CELEMS(szTemp) > (UINT)wsprintf(szTemp, TEXT("%s\\%s"), szInstallDir, szShortServiceName));
  2392. MYVERIFY(FALSE != CreateLayerDirectory(szTemp));
  2393. //
  2394. // We now need to write the registry key that the inf will use as the
  2395. // installation directory. See the CustomDestination section of the
  2396. // profile inf to see where this ties in.
  2397. //
  2398. if (plat.IsWin9x())
  2399. {
  2400. //
  2401. // Then we need to use the Short Name in the regkey or the inf will not install properly
  2402. //
  2403. MYVERIFY(0 != GetShortPathName(szInstallDir, szTemp, CELEMS(szTemp)));
  2404. lstrcpyn(szInstallDir, szTemp, MAX_PATH); // szTemp is larger than szInstallDir, we only use MAX_PATH chars
  2405. }
  2406. if (ERROR_SUCCESS != RegCreateKey(hBaseKey, pszRegInfCommKeyPath, &hKey))
  2407. {
  2408. CMASSERTMSG(FALSE, TEXT("InstallInf -- Unable to create the Inf Communication Key"));
  2409. MYVERIFY(0 != LoadString(hInstance, IDS_UNEXPECTEDERR, szTemp, CELEMS(szTemp)));
  2410. MessageBox(NULL, szTemp, szServiceName, MB_OK);
  2411. hrReturn = E_FAIL;
  2412. goto exit;
  2413. }
  2414. //
  2415. // We now need to create the value with our szInstallDir string.
  2416. //
  2417. dwType = REG_SZ;
  2418. dwSize = lstrlen(szInstallDir);
  2419. if (ERROR_SUCCESS != RegSetValueEx(hKey, c_pszProfileInstallPath, NULL, dwType,
  2420. (CONST BYTE *)szInstallDir, dwSize))
  2421. {
  2422. CMASSERTMSG(FALSE, TEXT("InstallInf -- Unable to set the Profile Install Path value."));
  2423. MYVERIFY(0 != LoadString(hInstance, IDS_UNEXPECTEDERR, szTemp, CELEMS(szTemp)));
  2424. MessageBox(NULL, szTemp, szServiceName, MB_OK);
  2425. MYVERIFY(ERROR_SUCCESS == RegCloseKey(hKey));
  2426. hrReturn = E_FAIL;
  2427. goto exit;
  2428. }
  2429. MYVERIFY(ERROR_SUCCESS == RegCloseKey(hKey));
  2430. }
  2431. else
  2432. {
  2433. CMASSERTMSG(FALSE, TEXT("InstallInf -- Unable to resolve the Install Directory."));
  2434. MYVERIFY(0 != LoadString(hInstance, IDS_UNEXPECTEDERR, szTemp, CELEMS(szTemp)));
  2435. MessageBox(NULL, szTemp, szServiceName, MB_OK);
  2436. hrReturn = E_FAIL;
  2437. goto exit;
  2438. }
  2439. //
  2440. // Install the Profile Files and create the mappings entry
  2441. //
  2442. if (bInstallForAllUsers)
  2443. {
  2444. hrTemp = LaunchInfSection(szInfFile, TEXT("DefaultInstall"), szTitle, bSilent);
  2445. MYDBGASSERT(SUCCEEDED(hrTemp));
  2446. bMustReboot = ((HRESULT)ERROR_SUCCESS_REBOOT_REQUIRED == hrTemp) ? TRUE: bMustReboot;
  2447. //
  2448. // Still launch this for Legacy (read MSN online setup reasons, perhaps others)
  2449. //
  2450. hrTemp = LaunchInfSection(szInfFile, TEXT("Xnstall_AllUser"), szTitle, bSilent);
  2451. MYDBGASSERT(SUCCEEDED(hrTemp));
  2452. bMustReboot = ((HRESULT)ERROR_SUCCESS_REBOOT_REQUIRED == hrTemp) ? TRUE: bMustReboot;
  2453. }
  2454. else
  2455. {
  2456. hrTemp = LaunchInfSection(szInfFile, TEXT("DefaultInstall_SingleUser"), szTitle, bSilent);
  2457. MYDBGASSERT(SUCCEEDED(hrTemp));
  2458. bMustReboot = ((HRESULT)ERROR_SUCCESS_REBOOT_REQUIRED == hrTemp) ? TRUE: bMustReboot;
  2459. //
  2460. // Still launch this for Legacy (I doubt anyone is using this but kept
  2461. // for consistency with All User which at least MSN was using)
  2462. //
  2463. hrTemp = LaunchInfSection(szInfFile, TEXT("Xnstall_Private"), szTitle, bSilent);
  2464. MYDBGASSERT(SUCCEEDED(hrTemp));
  2465. bMustReboot = ((HRESULT)ERROR_SUCCESS_REBOOT_REQUIRED == hrTemp) ? TRUE: bMustReboot;
  2466. //
  2467. // Write the single user mappings key in code since parsing is involved.
  2468. //
  2469. if (!WriteSingleUserProfileMappings(szInstallDir, szShortServiceName, szServiceName))
  2470. {
  2471. CMASSERTMSG(FALSE, TEXT("InstallInf -- WriteSingleUserProfileMappings Failed."));
  2472. MYVERIFY(0 != LoadString(hInstance, IDS_UNEXPECTEDERR, szTemp, CELEMS(szTemp)));
  2473. MessageBox(NULL, szTemp, szServiceName, MB_OK);
  2474. hrReturn = E_FAIL;
  2475. goto exit;
  2476. }
  2477. }
  2478. //
  2479. // Install the CM bits as necessary
  2480. //
  2481. if (bInstallCm)
  2482. {
  2483. MYDBGASSERT(FALSE == plat.IsNT51());
  2484. //
  2485. // First, we must extract the CM binaries from the binaries
  2486. // executable/cab to the cmbins sub dir.
  2487. //
  2488. wsprintf(szTemp, TEXT("%scmbins\\"), g_szProfileSourceDir);
  2489. hrTemp = ExtractCmBinsFromExe(g_szProfileSourceDir, szTemp);
  2490. if (SUCCEEDED(hrTemp))
  2491. {
  2492. TCHAR szSource [MAX_PATH+1] = {0};
  2493. TCHAR szDest [MAX_PATH+1] = {0};
  2494. if (plat.IsNT5())
  2495. {
  2496. //
  2497. // Copy cmexcept.cat to the cmbins dir
  2498. //
  2499. MYVERIFY(CELEMS(szDest) > (UINT)wsprintf(szDest, TEXT("%s%s"), szTemp, TEXT("cmexcept.cat")));
  2500. MYVERIFY(CELEMS(szSource) > (UINT)wsprintf(szSource, TEXT("%s%s"), g_szProfileSourceDir, TEXT("cmexcept.cat")));
  2501. MYVERIFY(CopyFile(szSource, szDest, FALSE)); // FALSE == bFailIfExists
  2502. //
  2503. // Check to see if we need to uninstall a previous CM exception inf
  2504. // and uninstall it as necessary.
  2505. //
  2506. hrTemp = UninstallExistingCmException();
  2507. MYDBGASSERT((S_OK == hrTemp) || (S_FALSE == hrTemp));
  2508. //
  2509. // Finally, install the CM bits
  2510. //
  2511. hrTemp = InstallWhistlerCmOnWin2k(szTemp);
  2512. if (FAILED(hrTemp))
  2513. {
  2514. if (!bSilent)
  2515. {
  2516. MYVERIFY(0 != LoadString(hInstance, IDS_WIN2K_CM_INSTALL_FAILED, szTemp, CELEMS(szTemp)));
  2517. MessageBox(NULL, szTemp, szServiceName, MB_OK | MB_ICONEXCLAMATION);
  2518. }
  2519. }
  2520. else
  2521. {
  2522. //
  2523. // Make sure to set permissions on the registry keys to avoid issues with non Admins uninstalling
  2524. // the CM binaries. If user's have permissions to the uninstall registry keys but not to
  2525. // files in system32 we can get ourselves in a wierd state. Fix this by locking down the reg permissions.
  2526. //
  2527. hrTemp = SetPermissionsOnWin2kExceptionUninstallRegKeys();
  2528. MYVERIFY(SUCCEEDED(hrTemp));
  2529. }
  2530. }
  2531. else
  2532. {
  2533. //
  2534. // Okay, we need to copy the instcm.inf file to the cmbins dir and then
  2535. // call InstallCm
  2536. //
  2537. LPCTSTR ArrayOfFileNames[] = {
  2538. TEXT("cnet16.dll"),
  2539. TEXT("ccfg95.dll"),
  2540. TEXT("cmutoa.dll"),
  2541. TEXT("instcm.inf") // instcm.inf must be last so it is given to InstallCm correctly.
  2542. };
  2543. for (int i = 0; i < (sizeof(ArrayOfFileNames)/sizeof(LPCTSTR)); i++)
  2544. {
  2545. MYVERIFY(CELEMS(szDest) > (UINT)wsprintf(szDest, TEXT("%s%s"), szTemp, ArrayOfFileNames[i]));
  2546. MYVERIFY(CELEMS(szSource) > (UINT)wsprintf(szSource, TEXT("%s%s"), g_szProfileSourceDir, ArrayOfFileNames[i]));
  2547. MYVERIFY(CopyFile(szSource, szDest, FALSE)); // FALSE == bFailIfExists
  2548. }
  2549. hrTemp = InstallCm(hInstance, szDest);
  2550. }
  2551. MYDBGASSERT(SUCCEEDED(hrTemp));
  2552. bMustReboot = ((HRESULT)ERROR_SUCCESS_REBOOT_REQUIRED == hrTemp) ? TRUE: bMustReboot;
  2553. }
  2554. else
  2555. {
  2556. CMASSERTMSG(FALSE, TEXT("InstallInf -- ExtractCmBinsFromExe failed!"));
  2557. }
  2558. }
  2559. //
  2560. // Now Create the Connectoid. Even if it fails, continue to install.
  2561. //
  2562. if (GetPhoneBookPath(szInstallDir, &pszPhonebook, bInstallForAllUsers))
  2563. {
  2564. BOOL bReturn = WriteCmPhonebookEntry(szServiceName, pszPhonebook, szCmsFile);
  2565. if (!bReturn && plat.IsAtLeastNT5())
  2566. {
  2567. CMASSERTMSG(FALSE, TEXT("CMSTP Failed to create a pbk entry on NT5, exiting."));
  2568. hrReturn = E_FAIL;
  2569. goto exit;
  2570. }
  2571. if (bInstallForAllUsers && plat.IsAtLeastNT5())
  2572. {
  2573. MYVERIFY(AllowAccessToWorld(pszPhonebook));
  2574. }
  2575. }
  2576. else if (plat.IsAtLeastNT5())
  2577. {
  2578. CMASSERTMSG(FALSE, TEXT("CMSTP Failed to get a pbk path on NT5, exiting."));
  2579. hrReturn = E_FAIL;
  2580. goto exit;
  2581. }
  2582. //
  2583. // Now we have all the files installed and the pbk entry written,
  2584. // finally create the desktop shortcut/GUID
  2585. //
  2586. if ((plat.IsWin9x()) || (plat.IsNT4()))
  2587. {
  2588. //
  2589. // If we have a Legacy install, then we need to create a desktop icon
  2590. //
  2591. if (!bNoLegacyIcon)
  2592. {
  2593. hrTemp = LaunchInfSection(szInfFile, TEXT("Xnstall_Legacy"), szTitle, bSilent);
  2594. MYDBGASSERT(SUCCEEDED(hrTemp));
  2595. bMustReboot = ((HRESULT)ERROR_SUCCESS_REBOOT_REQUIRED == hrTemp) ? TRUE: bMustReboot;
  2596. }
  2597. }
  2598. else
  2599. {
  2600. //
  2601. // Create a desktop shortcut if necessary
  2602. //
  2603. DeleteNT5ShortcutFromPathAndName(hInstance, szServiceName,
  2604. bInstallForAllUsers ? CSIDL_COMMON_DESKTOPDIRECTORY : CSIDL_DESKTOPDIRECTORY);
  2605. if (bCreateDesktopIcon)
  2606. {
  2607. HRESULT hr = CreateNT5ProfileShortcut(szServiceName, pszPhonebook, bInstallForAllUsers);
  2608. MYVERIFY(SUCCEEDED(hr));
  2609. }
  2610. }
  2611. //
  2612. // The profile is now basically installed. Before doing any post install commands, lets check to see
  2613. // if the caller asked us to set this connection as the default connection. If so, then
  2614. // lets set it here.
  2615. //
  2616. if (bSetAsDefault)
  2617. {
  2618. MYVERIFY(SetThisConnectionAsDefault(szServiceName));
  2619. }
  2620. //
  2621. // if we have a preshared key, give it to RAS
  2622. //
  2623. if (pszPresharedKey)
  2624. {
  2625. if (plat.IsAtLeastNT51())
  2626. {
  2627. pfnRasSetCredentialsSpec pfnSetCredentials;
  2628. hrReturn = E_FAIL;
  2629. if (FALSE == GetRasApis(NULL, NULL, NULL, NULL, NULL, &pfnSetCredentials))
  2630. {
  2631. CMASSERTMSG(FALSE, TEXT("CMSTP Failed to get RAS API RasSetCredentials, exiting."));
  2632. goto exit;
  2633. }
  2634. if (lstrlen(pszPresharedKey) > PWLEN)
  2635. {
  2636. CMASSERTMSG(FALSE, TEXT("preshared key is larger than RasSetCredentials can handle!"));
  2637. goto exit;
  2638. }
  2639. RASCREDENTIALS * pRasCreds = NULL;
  2640. pRasCreds = (RASCREDENTIALS *) CmMalloc(sizeof(RASCREDENTIALS));
  2641. if (NULL == pRasCreds)
  2642. {
  2643. hrReturn = E_OUTOFMEMORY;
  2644. goto exit;
  2645. }
  2646. pRasCreds->dwSize = sizeof(RASCREDENTIALS);
  2647. pRasCreds->dwMask = RASCM_PreSharedKey;
  2648. lstrcpyn(pRasCreds->szPassword, pszPresharedKey, lstrlen(pszPresharedKey) + 1);
  2649. if (0 != pfnSetCredentials(pszPhonebook, szServiceName, pRasCreds, FALSE)) // FALSE => set the credentials
  2650. {
  2651. CMASSERTMSG(FALSE, TEXT("CMSTP RasSetCredentials failed, exiting."));
  2652. CmFree(pRasCreds);
  2653. goto exit;
  2654. }
  2655. CmFree(pRasCreds);
  2656. hrReturn = S_OK;
  2657. }
  2658. else if ((plat.IsNT4() || plat.IsWin9x()))
  2659. {
  2660. SafeNetLinkageStruct SnLinkage = {0};
  2661. if (IsSafeNetClientAvailable() && LinkToSafeNet(&SnLinkage))
  2662. {
  2663. DATA_BLOB DataBlob = {0};
  2664. DataBlob.cbData = (lstrlen(pszPresharedKey) + 1)*sizeof(TCHAR);
  2665. DataBlob.pbData = (BYTE*)pszPresharedKey;
  2666. if (FALSE == SnLinkage.pfnSnPolicySet(SN_L2TPPRESHR, (void*)&DataBlob))
  2667. {
  2668. CMASSERTMSG(FALSE, TEXT("CMSTP SnPolicySet failed, exiting."));
  2669. UnLinkFromSafeNet(&SnLinkage);
  2670. goto exit;
  2671. }
  2672. //
  2673. // Now call SnPolicyReload so the SafeNet driver will take the settings changes
  2674. //
  2675. if (FALSE == SnLinkage.pfnSnPolicyReload())
  2676. {
  2677. //
  2678. // Then we need to ask the user to reboot after finishing the install for the changes to take affect.
  2679. //
  2680. bMustReboot = TRUE;
  2681. }
  2682. }
  2683. else
  2684. {
  2685. CMTRACE(TEXT("CMSTP tried to set PSK but SafeNet interface unavailable."));
  2686. }
  2687. UnLinkFromSafeNet(&SnLinkage);
  2688. }
  2689. }
  2690. //
  2691. // Do any postinstall cmds here
  2692. //
  2693. LPTSTR pszPostInstallSection;
  2694. if (bInstallForAllUsers)
  2695. {
  2696. pszPostInstallSection = TEXT("PostInstall");
  2697. }
  2698. else
  2699. {
  2700. pszPostInstallSection = TEXT("PostInstall_Single");
  2701. }
  2702. hrTemp = LaunchInfSection(szInfFile, pszPostInstallSection, szTitle, bSilent);
  2703. MYDBGASSERT(SUCCEEDED(hrTemp));
  2704. bMustReboot = ((HRESULT)ERROR_SUCCESS_REBOOT_REQUIRED == hrTemp) ? TRUE: bMustReboot;
  2705. //
  2706. // Delete the temporary reg key that we used to communicate the install path to the inf
  2707. //
  2708. if (ERROR_SUCCESS == RegOpenKeyEx(hBaseKey, pszRegInfCommKeyPath,
  2709. 0, KEY_ALL_ACCESS, &hKey))
  2710. {
  2711. MYVERIFY(ERROR_SUCCESS == RegDeleteValue(hKey, c_pszProfileInstallPath));
  2712. MYVERIFY(ERROR_SUCCESS == RegCloseKey(hKey));
  2713. }
  2714. else
  2715. {
  2716. CMASSERTMSG(FALSE, TEXT("Unable to delete the ProfileInstallPath temporary Reg value."));
  2717. }
  2718. //
  2719. // Refresh the desktop so that any GUID or shortcut changes will appear
  2720. //
  2721. RefreshDesktop();
  2722. //
  2723. // For Win98 and Millennium, we write an App Compatibility flag in order to
  2724. // fix SetForegroundWindow. Refer also to Q135788 for more details of the
  2725. // original fix (which requires this extra code on Win9x to actually work).
  2726. //
  2727. // This fixes Whistler bugs 41696 and 90576.
  2728. //
  2729. if (plat.IsWin98())
  2730. {
  2731. if (!WriteProfileString(TEXT("Compatibility95"), TEXT("CMMON32"), TEXT("0x00000002")))
  2732. {
  2733. CMTRACE(TEXT("InstallInf - failed to write app compat entry for CMMON32 to fix SetForegroundWindow"));
  2734. }
  2735. }
  2736. //
  2737. // We are finally completed. If we need to reboot, show the user the reboot prompt.
  2738. // Otherwise, show the user a completion message.
  2739. //
  2740. if (bMustReboot)
  2741. {
  2742. MYVERIFY(0 != LoadString(hInstance, IDS_REBOOT_MSG, szTemp, CELEMS(szTemp)));
  2743. int iRes = MessageBoxEx(NULL,
  2744. szTemp,
  2745. szServiceName,
  2746. MB_YESNO | MB_DEFBUTTON1 | MB_ICONWARNING | MB_SETFOREGROUND,
  2747. LANG_USER_DEFAULT);
  2748. if (IDYES == iRes)
  2749. {
  2750. //
  2751. // Shutdown Windows
  2752. //
  2753. DWORD dwReason = plat.IsAtLeastNT51() ? (SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_INSTALLATION) : 0;
  2754. MyExitWindowsEx(EWX_REBOOT, dwReason);
  2755. }
  2756. }
  2757. else if (!bSilent)
  2758. {
  2759. //
  2760. // Instead of giving the user a message box, we will launch the profile
  2761. // for them. (NTRAID 201307)
  2762. //
  2763. if (plat.IsAtLeastNT5())
  2764. {
  2765. pCmstpMutex->Unlock(); //NTRAID 310478
  2766. }
  2767. MYVERIFY(CELEMS(szTemp) > (UINT)wsprintf(szTemp, TEXT("%s\\%s.cmp"),
  2768. szInstallDir, szShortServiceName));
  2769. LaunchProfile(szTemp, szServiceName, pszPhonebook, bInstallForAllUsers);
  2770. }
  2771. exit:
  2772. CmFree(pszPresharedKey);
  2773. CmFree(pszPhonebook);
  2774. return hrReturn;
  2775. }