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

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