//+---------------------------------------------------------------------------- // // File: install.cpp // // Module: CMSTP.EXE // // Synopsis: This source file contains the code for installing CM profiles. // // Copyright (c) 1997-1999 Microsoft Corporation // // Author: quintinb Created 07/14/98 // //+---------------------------------------------------------------------------- #include "cmmaster.h" #include "installerfuncs.h" #include "winuserp.h" #include #include "tunl_str.h" #include "cmsecure.h" // linkdll is needed because of cmsecure #include "linkdll.h" // LinkToDll and BindLinkage #include "linkdll.cpp" // LinkToDll and BindLinkage #include "gppswithalloc.cpp" // // This global var, contains the path to the source files to install such as the // cmp, cms, and inf. (From the inf path passed in to InstallInf). // TCHAR g_szProfileSourceDir[MAX_PATH+1]; // This is really ugly, we need to consolidate our platform detection code between CM and // the setup components. BOOL IsNT() { CPlatform plat; return plat.IsNT(); } #define OS_NT (IsNT()) #include "cmexitwin.cpp" //+---------------------------------------------------------------------------- // // Function: CheckIeDllRequirements // // Synopsis: This function checks to see if the browser agnostic dlls are of // a sufficient version for CM to work, or if we should copy the // dlls we carry in the package with us. // // Arguments: CPlatform* pPlat - a CPlatform object // // Returns: BOOL - returns TRUE if all browser files meet the requirements, FALSE // if any one of the files fails to meet what CM needs. // // History: quintinb Created Header 5/24/99 // //+---------------------------------------------------------------------------- BOOL CheckIeDllRequirements(CPlatform* pPlat) { TCHAR szSysDir[MAX_PATH+1]; TCHAR szDllToCheck[MAX_PATH+1]; if(GetSystemDirectory(szSysDir, MAX_PATH)) { if (pPlat->IsWin9x()) { // // Need Advapi32.dll to be version 4.70.0.1215 or greater. // const DWORD c_dwRequiredAdvapi32Version = (4 << c_iShiftAmount) + 70; const DWORD c_dwRequiredAdvapi32BuildNumber = 1215; MYVERIFY(CELEMS(szDllToCheck) > (UINT)wsprintf(szDllToCheck, TEXT("%s%s"), szSysDir, TEXT("\\advapi32.dll"))); CVersion AdvApi32Version(szDllToCheck); if ((c_dwRequiredAdvapi32Version > AdvApi32Version.GetVersionNumber()) || ((c_dwRequiredAdvapi32Version == AdvApi32Version.GetVersionNumber()) && (c_dwRequiredAdvapi32BuildNumber > AdvApi32Version.GetBuildAndQfeNumber()))) { return FALSE; } // // Need comctl32.dll to be version 4.70.0.1146 or greater. // const DWORD c_dwRequiredComctl32Version = (4 << c_iShiftAmount) + 70; const DWORD c_dwRequiredComctl32BuildNumber = 1146; MYVERIFY(CELEMS(szDllToCheck) > (UINT)wsprintf(szDllToCheck, TEXT("%s%s"), szSysDir, TEXT("\\comctl32.dll"))); CVersion Comctl32Version(szDllToCheck); if ((c_dwRequiredComctl32Version > Comctl32Version.GetVersionNumber()) || ((c_dwRequiredComctl32Version == Comctl32Version.GetVersionNumber()) && (c_dwRequiredComctl32BuildNumber > Comctl32Version.GetBuildAndQfeNumber()))) { return FALSE; } // // Need rnaph.dll to be version 4.40.311.0 or greater. // const DWORD c_dwRequiredRnaphVersion = (4 << c_iShiftAmount) + 40; const DWORD c_dwRequiredRnaphBuildNumber = (311 << c_iShiftAmount); MYVERIFY(CELEMS(szDllToCheck) > (UINT)wsprintf(szDllToCheck, TEXT("%s%s"), szSysDir, TEXT("\\rnaph.dll"))); CVersion RnaphVersion(szDllToCheck); if ((c_dwRequiredRnaphVersion > RnaphVersion.GetVersionNumber()) || ((c_dwRequiredRnaphVersion == RnaphVersion.GetVersionNumber()) && (c_dwRequiredRnaphBuildNumber > RnaphVersion.GetBuildAndQfeNumber()))) { return FALSE; } } // // Need wininet.dll to be version 4.70.0.1301 or greater. // const DWORD c_dwRequiredWininetVersion = (4 << c_iShiftAmount) + 70; const DWORD c_dwRequiredWininetBuildNumber = 1301; MYVERIFY(CELEMS(szDllToCheck) > (UINT)wsprintf(szDllToCheck, TEXT("%s%s"), szSysDir, TEXT("\\wininet.dll"))); CVersion WininetVersion(szDllToCheck); if ((c_dwRequiredWininetVersion > WininetVersion.GetVersionNumber()) || ((c_dwRequiredWininetVersion == WininetVersion.GetVersionNumber()) && (c_dwRequiredWininetBuildNumber > WininetVersion.GetBuildAndQfeNumber()))) { return FALSE; } } else { return FALSE; } return TRUE; } //+---------------------------------------------------------------------------- // // Function: WriteSingleUserProfileMappings // // Synopsis: This function write the single user mappings key. // // Arguments: HINSTANCE hInstance - an Instance handle to load string resources with // LPCTSTR pszShortServiceName - short service name of the profile // LPCTSTR pszServiceName - Long service name of the profile // // Returns: BOOL - TRUE if successful // // History: quintinb Created 5/23/99 // //+---------------------------------------------------------------------------- BOOL WriteSingleUserProfileMappings(LPCTSTR pszInstallDir, LPCTSTR pszShortServiceName, LPCTSTR pszServiceName) { BOOL bReturn = FALSE; TCHAR szCmpFile [MAX_PATH+1]; TCHAR szTemp [MAX_PATH+1]; TCHAR szUserProfilePath [MAX_PATH+1]; HKEY hKey = NULL; // // Construct the Cmp Path // MYVERIFY(CELEMS(szCmpFile) > (UINT)wsprintf(szCmpFile, TEXT("%s\\%s.cmp"), pszInstallDir, pszShortServiceName)); // // Figure out the User Profile directory // DWORD dwChars = ExpandEnvironmentStrings(TEXT("%AppData%"), szUserProfilePath, MAX_PATH); if (dwChars && (MAX_PATH >= dwChars)) { // // We want to do a lstrcmpi but with only so many chars. Unfortunately this doesn't // exist in Win32 so we will use lstrcpyn into a temp buffer and then use lstrcmpi. // lstrcpyn(szTemp, szCmpFile, lstrlen(szUserProfilePath) + 1); if (0 == lstrcmpi(szTemp, szUserProfilePath)) { lstrcpy(szTemp, szCmpFile + lstrlen(szUserProfilePath)); lstrcpy(szCmpFile, TEXT("%AppData%")); lstrcat(szCmpFile, szTemp); } else { CMASSERTMSG(FALSE, TEXT("Unable to build the Single User Mappings key value, exiting.")); goto exit; } // // Okay, now we need to write out the single user mappings key // DWORD dwDisposition; LONG lResult = RegCreateKeyEx(HKEY_CURRENT_USER, c_pszRegCmMappings, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hKey, &dwDisposition); if (ERROR_SUCCESS == lResult) { DWORD dwType = REG_SZ; DWORD dwSize = lstrlen(szCmpFile) + 1; if (ERROR_SUCCESS != RegSetValueEx(hKey, pszServiceName, NULL, dwType, (CONST BYTE *)szCmpFile, dwSize)) { CMASSERTMSG(FALSE, TEXT("Unable to write the Single User Mappings key value, exiting.")); goto exit; } else { bReturn = TRUE; } } } else { CMASSERTMSG(FALSE, TEXT("Unable to expand the AppData String, exiting.")); goto exit; } exit: if (hKey) { MYVERIFY(ERROR_SUCCESS == RegCloseKey(hKey)); } return bReturn; } //+---------------------------------------------------------------------------- // // Function: ProcessPreferencesUI // // Synopsis: This function processes messages for either of the two dialogs used // to ask the user if they want a desktop shortcut. One dialog is for // non-admins and only contains the shortcut question, the other dialog // is for local admins and also contains whether the admin wants the // profile installed for all users or just for single users. // // // History: quintinb Created 2/19/98 // quintinb Renamed from ProcessAdminUI to ProcessPreferencesUI and // added new functionality 6/9/8 // quintinb removed mention of Start Menu Shortcut 2/17/99 // //+---------------------------------------------------------------------------- BOOL APIENTRY ProcessPreferencesUI( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { int iUiChoices; HKEY hKey; DWORD dwSize; DWORD dwTemp; DWORD dwType; InitDialogStruct* pDialogArgs = NULL; switch (message) { case WM_INITDIALOG: // // Look up the preferences for Desktop Shortcuts/Start Menu Links // in the registry and set them accordingly. // pDialogArgs = (InitDialogStruct*)lParam; if (pDialogArgs->bNoDesktopIcon) { MYVERIFY(0 != CheckDlgButton(hDlg, IDC_DESKTOP, FALSE)); } else { if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_CURRENT_USER, c_pszRegStickyUiDefault, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hKey, &dwTemp)) { // // The default of whether a desktop shortcut should be created is stored in the // registry. Get this value to populate the UI. (default is off) // dwType = REG_DWORD; dwSize = sizeof(DWORD); dwTemp = 0; RegQueryValueEx(hKey, c_pszRegDesktopShortCut, NULL, &dwType, (LPBYTE)&dwTemp, &dwSize); //lint !e534 MYVERIFY(0 != CheckDlgButton(hDlg, IDC_DESKTOP, dwTemp)); MYVERIFY(ERROR_SUCCESS == RegCloseKey(hKey)); } } // // Set the Window Text to the Profile Name // MYVERIFY(FALSE != SetWindowText(hDlg, pDialogArgs->pszTitle)); if (!(pDialogArgs->bSingleUser)) { CheckDlgButton(hDlg, IDC_ALLUSERS, TRUE); //lint !e534 this will fail if using the nochoice UI } else { CheckDlgButton(hDlg, IDC_YOURSELF, TRUE); //lint !e534 this will fail if using the nochoice UI } // // We return FALSE here but the focus is correctly set. // break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: // // Build the return value // if (IsDlgButtonChecked(hDlg, IDC_ALLUSERS) == BST_CHECKED) { iUiChoices = ALLUSERS; } else { iUiChoices = 0; } if (IsDlgButtonChecked(hDlg, IDC_DESKTOP)) { iUiChoices |= CREATEDESKTOPICON; } // // Make sure to save the users preferences for Desktop Icons // and Start Menu Links. // if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_CURRENT_USER, c_pszRegStickyUiDefault, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, &dwTemp)) { // // Store the current state of whether we should create a desktop shortcut // dwTemp = IsDlgButtonChecked(hDlg, IDC_DESKTOP); MYVERIFY(ERROR_SUCCESS == RegSetValueEx(hKey, c_pszRegDesktopShortCut, 0, REG_DWORD, (LPBYTE)&dwTemp, sizeof(DWORD))); MYVERIFY(ERROR_SUCCESS == RegCloseKey(hKey)); } MYVERIFY(FALSE != EndDialog(hDlg, iUiChoices)); return (TRUE); case IDCANCEL: MYVERIFY(FALSE != EndDialog(hDlg, -1)); return TRUE; default: break; } break; case WM_CLOSE: MYVERIFY(FALSE != EndDialog(hDlg, -1)); return TRUE; default: return FALSE; } return FALSE; } //+---------------------------------------------------------------------------- // // Function: InstallCm // // Synopsis: This function calls LaunchInfSection on the appropriate // install section to install Connection Manager. It also installs // the browser files as appropriate. // // Arguments: HINSTANCE hInstance - Instance handle for strings // LPCTSTR szInfPath - Full path to the inf // // Returns: HRESULT - standard com codes, could return ERROR_SUCCESS_REBOOT_REQUIRED // so the caller must check for this case and ask for a reboot // if required. // // History: quintinb Created 8/12/98 // quintinb Moved Browser file installation code here, since it is // part of the installation of CM. 10-2-98 // //+---------------------------------------------------------------------------- HRESULT InstallCm(HINSTANCE hInstance, LPCTSTR szInfPath) { HRESULT hr = E_UNEXPECTED; MYDBGASSERT((szInfPath) && (TEXT('\0') != szInfPath[0])); // // Load the Cmstp Title just in case we need to show error messages. // TCHAR szTitle[MAX_PATH+1] = {TEXT("")}; MYVERIFY(0 != LoadString(hInstance, IDS_CMSTP_TITLE, szTitle, MAX_PATH)); MYDBGASSERT(TEXT('\0') != szTitle[0]); // // Make sure that the Inf File exists // if (!FileExists(szInfPath)) { CMTRACE1(TEXT("InstallCm -- Can't find %s, the inputted Inf file."), szInfPath); return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); } CPlatform plat; TCHAR szInstallSection[MAX_PATH+1] = {TEXT("")}; if (plat.IsNT()) { MYVERIFY(CELEMS(szInstallSection) > (UINT)wsprintf(szInstallSection, TEXT("DefaultInstall_NT"))); } else { MYVERIFY(CELEMS(szInstallSection) > (UINT)wsprintf(szInstallSection, TEXT("DefaultInstall"))); } hr = LaunchInfSection(szInfPath, szInstallSection, szTitle, TRUE); // bQuiet = TRUE return hr; } //+---------------------------------------------------------------------------- // // Function: InstallWhistlerCmOnWin2k // // Synopsis: This function uses the CM exception inf (cmexcept.inf) to install // the Whistler CM binaries on Win2k. // // Arguments: LPCSTR pszSourceDir - source directory for cmexcept.inf and CM // binaries, including the trailing slash. // // Returns: HRESULT - standard com codes, could return ERROR_SUCCESS_REBOOT_REQUIRED // which means the caller needs to request a reboot. // // History: quintinb Created 02/09/2001 // //+---------------------------------------------------------------------------- HRESULT InstallWhistlerCmOnWin2k(LPCSTR pszSourceDir) { CPlatform cmplat; HRESULT hr = E_UNEXPECTED; LPSTR pszInfFile = NULL; LPCSTR c_pszExceptionInf = "cmexcept.inf"; LPCSTR c_pszInstallSection = "DefaultInstall"; LPCSTR c_pszUnInstallSection = "DefaultUninstall_NoPrompt"; if (cmplat.IsNT5()) { if (pszSourceDir && pszSourceDir[0]) { DWORD dwSize = sizeof(CHAR)*(lstrlenA(pszSourceDir) + lstrlenA(c_pszExceptionInf) + 1); pszInfFile = (LPSTR)CmMalloc(dwSize); if (pszInfFile) { wsprintf(pszInfFile, "%s%s", pszSourceDir, c_pszExceptionInf); if (FileExists(pszInfFile)) { hr = CallLaunchInfSectionEx(pszInfFile, c_pszInstallSection, (ALINF_BKINSTALL | ALINF_QUIET)); if (FAILED(hr)) { CMTRACE1(TEXT("InstallWhistlerCmOnWin2k -- CallLaunchInfSectionEx failed with hr=0x%x"), hr); HRESULT hrTemp = CallLaunchInfSectionEx(pszInfFile, c_pszUnInstallSection, (ALINF_ROLLBKDOALL | ALINF_QUIET)); CMTRACE1(TEXT("InstallWhistlerCmOnWin2k -- Rolling back. CallLaunchInfSectionEx returned hr=0x%x"), hrTemp); } } else { hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); } } else { hr = E_OUTOFMEMORY; } } else { hr = E_INVALIDARG; } } else { hr = HRESULT_FROM_WIN32(ERROR_INSTALL_PLATFORM_UNSUPPORTED); // kind of a double use of this error } CmFree(pszInfFile); return hr; } //+---------------------------------------------------------------------------- // // Function: UpdateCmpDataFromExistingProfile // // Synopsis: This function enumerates all of the keys in all of the sections // of an existing cmp file and copies them to the cmp file to be // installed. This function copies all of the data in the existing // cmp unless that data already exists in the cmp to install. This // allows Admins to preseed cmp files and have their settings override // what the user currently has in their cmp. // // Arguments: LPCTSTR pszShortServiceName - Short Service name of the profile // LPCTSTR szCurrentCmp - Full path to the currently installed cmp // LPCTSTR szCmpToBeInstalled - Full path to the cmp to install // // Returns: BOOL - TRUE if the cmp is copied and updated properly // // History: quintinb Created 03/16/99 // quintinb rewrote for Whistler bug 18021 03/05/00 // //+---------------------------------------------------------------------------- BOOL UpdateCmpDataFromExistingProfile(LPCTSTR pszShortServiceName, LPCTSTR pszCurrentCmp, LPCTSTR pszCmpToBeInstalled) { if((NULL == pszShortServiceName) && (TEXT('\0') == pszShortServiceName[0]) && (NULL == pszCurrentCmp) && (TEXT('\0') == pszCurrentCmp[0]) && (NULL == pszCmpToBeInstalled) && (TEXT('\0') == pszCmpToBeInstalled[0])) { CMASSERTMSG(FALSE, TEXT("UpdateCmpDataFromExistingProfile -- Invalid parameter.")); return FALSE; } BOOL bReturn = FALSE; BOOL bExitLoop = FALSE; DWORD dwSize = MAX_PATH; DWORD dwReturnedSize; LPTSTR pszAllSections = NULL; LPTSTR pszAllKeysInCurrentSection = NULL; LPTSTR pszCurrentSection = NULL; LPTSTR pszCurrentKey = NULL; TCHAR szData[MAX_PATH+1]; // // First lets get all of the sections from the existing cmp // pszAllSections = (TCHAR*)CmMalloc(dwSize*sizeof(TCHAR)); do { MYDBGASSERT(pszAllSections); if (pszAllSections) { dwReturnedSize = GetPrivateProfileString(NULL, NULL, TEXT(""), pszAllSections, dwSize, pszCurrentCmp); if (dwReturnedSize == (dwSize - 2)) { // // The buffer is too small, lets allocate a bigger one // dwSize = 2*dwSize; if (dwSize > 1024*1024) { CMASSERTMSG(FALSE, TEXT("UpdateCmpDataFromExistingProfile -- Allocation above 1MB, bailing out.")); goto exit; } pszAllSections = (TCHAR*)CmRealloc(pszAllSections, dwSize*sizeof(TCHAR)); } else if (0 == dwReturnedSize) { // // We got an error, lets exit. // CMASSERTMSG(FALSE, TEXT("UpdateCmpDataFromExistingProfile -- GetPrivateProfileString returned failure.")); goto exit; } else { bExitLoop = TRUE; } } else { goto exit; } } while (!bExitLoop); // // Okay, now we have all of the sections in the existing cmp file. Lets enumerate // all of the keys in each section and see which ones need to be copied over. // pszCurrentSection = pszAllSections; dwSize = MAX_PATH; pszAllKeysInCurrentSection = (TCHAR*)CmMalloc(dwSize*sizeof(TCHAR)); while (TEXT('\0') != pszCurrentSection[0]) { // // Get all of the keys in the current section // bExitLoop = FALSE; do { if (pszAllKeysInCurrentSection) { dwReturnedSize = GetPrivateProfileString(pszCurrentSection, NULL, TEXT(""), pszAllKeysInCurrentSection, dwSize, pszCurrentCmp); if (dwReturnedSize == (dwSize - 2)) { // // The buffer is too small, lets allocate a bigger one // dwSize = 2*dwSize; if (dwSize > 1024*1024) { CMASSERTMSG(FALSE, TEXT("UpdateCmpDataFromExistingProfile -- Allocation above 1MB, bailing out.")); goto exit; } pszAllKeysInCurrentSection = (TCHAR*)CmRealloc(pszAllKeysInCurrentSection, dwSize*sizeof(TCHAR)); } else if (0 == dwReturnedSize) { // // We got an error, lets exit. // CMASSERTMSG(FALSE, TEXT("UpdateCmpDataFromExistingProfile -- GetPrivateProfileString returned failure.")); goto exit; } else { bExitLoop = TRUE; } } else { goto exit; } } while (!bExitLoop); // // Now process all of the keys in the current section // pszCurrentKey = pszAllKeysInCurrentSection; while (TEXT('\0') != pszCurrentKey[0]) { // // Try to get the value of the key from the new cmp. If it // doesn't exist, then copy of the old cmp value. If it // does exist keep the new cmp value and ignore the old one. // dwReturnedSize = GetPrivateProfileString(pszCurrentSection, pszCurrentKey, TEXT(""), szData, MAX_PATH, pszCmpToBeInstalled); if (0 == dwReturnedSize) { // // Then we have a value in the old profile that we don't have in the new profile. // dwReturnedSize = GetPrivateProfileString(pszCurrentSection, pszCurrentKey, TEXT(""), szData, MAX_PATH, pszCurrentCmp); if (dwReturnedSize) { MYVERIFY(0 != WritePrivateProfileString(pszCurrentSection, pszCurrentKey, szData, pszCmpToBeInstalled)); } } // // Advance to the next key in pszAllKeysInCurrentSection // pszCurrentKey = pszCurrentKey + lstrlen(pszCurrentKey) + 1; } // // Now advance to the next string in pszAllSections // pszCurrentSection = pszCurrentSection + lstrlen(pszCurrentSection) + 1; } // // Flush the updated cmp // WritePrivateProfileString(NULL, NULL, NULL, pszCmpToBeInstalled); //lint !e534 this call will return 0 bReturn = TRUE; exit: CmFree(pszAllSections); CmFree(pszAllKeysInCurrentSection); return bReturn; } //+---------------------------------------------------------------------------- // // Function: MigrateCmpData // // Synopsis: This function checks to see if a profile of the same long service // and short service name is already installed. If it is, it migrates // the existing cmp data to the cmp file that is to be installed. // If the same piece of data exists in both profiles the data in the // cmp to be installed wins (allows admins to pre-seed data in the // cmp and override what users have picked). // // Arguments: HINSTANCE hInstance - Instance handle for string resources // BOOL bInstallForAllUsers - whether this is an all users profile or not // LPCTSTR pszServiceName - ServiceName of the current profile // LPCTSTR pszShortServiceName - Short Service name of the current profile // BOOL bSilent - whether messages to the user can be displayed or not // // Returns: int - returns -1 on error, otherwise TRUE or FALSE depending on if a same name // profile was discovered // // History: quintinb Created 9/8/98 // //+---------------------------------------------------------------------------- BOOL MigrateCmpData(HINSTANCE hInstance, BOOL bInstallForAllUsers, LPCTSTR pszServiceName, LPCTSTR pszShortServiceName, BOOL bSilent) { // // Check the parameters // if ((NULL == pszShortServiceName) || (TEXT('\0') == pszShortServiceName[0]) || (NULL == pszServiceName) || (TEXT('\0') == pszServiceName[0])) { CMASSERTMSG(FALSE, TEXT("MigrateCmpData -- Invalid Parameter")); return FALSE; } BOOL bReturn = TRUE; DWORD dwSize = MAX_PATH; HKEY hKey; HKEY hBaseKey = bInstallForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; TCHAR szExistingCmp[MAX_PATH+1]; TCHAR szCmpToBeInstalled[MAX_PATH+1]; TCHAR szFmtString[2*MAX_PATH+1] = TEXT(""); TCHAR szMsg[2*MAX_PATH+1] = TEXT(""); // // Read the mappings value // LONG lResult = RegOpenKeyEx(hBaseKey, c_pszRegCmMappings, 0, KEY_READ, &hKey); if (ERROR_SUCCESS == lResult) { lResult = RegQueryValueEx(hKey, pszServiceName, NULL, NULL, (LPBYTE)szFmtString, &dwSize); if (ERROR_SUCCESS == lResult) { // // Expand the path in case it contains environment vars // if (0 == ExpandEnvironmentStrings(szFmtString, szExistingCmp, MAX_PATH)) { CMASSERTMSG(FALSE, TEXT("MigrateCmpData -- Unable to expand environment strings, not migrating cmp data.")); goto exit; } // // If the file doesn't exist we have nothing to get cmp settings from ... thus // lets just happily exit. // if (!FileExists(szExistingCmp)) { goto exit; } // // Check to make sure that the Short Service Names agree for the two profiles // CFileNameParts ExistingCmpParts(szExistingCmp); if (0 != lstrcmpi(pszShortServiceName, ExistingCmpParts.m_FileName)) { if (!bSilent) { MYVERIFY(0 != LoadString(hInstance, IDS_SAME_LS_DIFF_SS, szFmtString, CELEMS(szFmtString))); MYVERIFY(CELEMS(szMsg) > (UINT)wsprintf(szMsg, szFmtString, pszShortServiceName, ExistingCmpParts.m_FileName, pszServiceName)); MessageBox(NULL, szMsg, pszServiceName, MB_OK); } bReturn = -1; goto exit; } // // Get the path of the cmp to install // MYVERIFY(CELEMS(szCmpToBeInstalled) > (UINT)wsprintf(szCmpToBeInstalled, TEXT("%s%s.cmp"), g_szProfileSourceDir, pszShortServiceName)); if (FALSE == UpdateCmpDataFromExistingProfile(pszShortServiceName, szExistingCmp, szCmpToBeInstalled)) { bReturn = -1; } } } exit: return bReturn; } //+---------------------------------------------------------------------------- // // Function: NeedCM10Upgrade // // Synopsis: This function detects and prepares data for the same name upgrade of a CM 1.0 profile. // Thus if you pass in a short service name and a service name, the // function detects if this profile is already installed for all users. // If it is, then the function checks the profile version stamps in the cmp. // If the current version isn't already newer and the user isn't a non-admin // on NT5, then we prompt the user if they want to upgrade the current install. // If they choose to upgrade then this function migrates the cmp data and // finds the uninstall inf. // // Arguments: HINSTANCE hInstance - Instance handle for string resources // LPCTSTR pszServiceName - ServiceName of the current profile // LPCTSTR pszShortServiceName - Short Service name of the current profile // LPTSTR pszOldInfPath - Out param for the old inf path, if the same name // upgrade is needed. // // Returns: int - returns -1 on error, otherwise TRUE or FALSE depending on if a same name // profile was discovered // // History: quintinb Created 9/8/98 // //+---------------------------------------------------------------------------- int NeedCM10Upgrade(HINSTANCE hInstance, LPCTSTR pszServiceName, LPCTSTR pszShortServiceName, LPTSTR pszOldInfPath, BOOL bSilent, CPlatform* plat) { HKEY hKey; TCHAR szFmtString[2*MAX_PATH+1] = TEXT(""); TCHAR szMsg[2*MAX_PATH+1] = TEXT(""); const int c_iCM12ProfileVersion = 4; MYDBGASSERT((NULL != pszShortServiceName) && (TEXT('\0') != pszShortServiceName[0])); MYDBGASSERT((NULL != pszServiceName) && (TEXT('\0') != pszServiceName[0])); if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_pszRegCmMappings, 0, KEY_READ, &hKey)) { int iCurrentCmpVersion; int iCmpVersionToInstall; TCHAR szCurrentCmp[MAX_PATH+1]; TCHAR szCmpToBeInstalled[MAX_PATH+1]; DWORD dwSize = MAX_PATH; if (ERROR_SUCCESS == RegQueryValueEx(hKey, pszServiceName, NULL, NULL, (LPBYTE)szCurrentCmp, &dwSize)) { // // First check to see that the file really exists. It is a somewhat probable case // that the users will have deleted their Profile files but left the registry // keys intact (they didn't uninstall it). In fact, MSN 2.5 and 2.6 operate this // way. Thus if the cmp doesn't actually exist then we don't need a same name // upgrade. // if (!FileExists(szCurrentCmp)) { CMASSERTMSG(FALSE, TEXT("Detected a CM 1.0 Upgrade, but the cmp didn't exist. Not Processing the upgrade.")); return FALSE; } // // Check to make sure that the Short Service Names agree for the two profiles // CFileNameParts CurrentCmpParts(szCurrentCmp); if (0 != lstrcmpi(pszShortServiceName, CurrentCmpParts.m_FileName)) { if (!bSilent) { MYVERIFY(0 != LoadString(hInstance, IDS_SAME_LS_DIFF_SS, szFmtString, CELEMS(szFmtString))); MYVERIFY(CELEMS(szMsg) > (UINT)wsprintf(szMsg, szFmtString, pszShortServiceName, CurrentCmpParts.m_FileName, pszServiceName)); MessageBox(NULL, szMsg, pszServiceName, MB_OK); } return -1; } // // Then we have the same servicename profile installed as an all users install. // Check the version number in the CMP. If the same version or less then we want // to run the same name upgrade. If the current version is more recent, then // we want to prevent the user from installing. // // // Get Currently Installed Profile version // iCurrentCmpVersion = GetPrivateProfileInt(c_pszCmSectionProfileFormat, c_pszVersion, 0, szCurrentCmp); // // Get the version of the profile to install // MYVERIFY(CELEMS(szCmpToBeInstalled) > (UINT)wsprintf(szCmpToBeInstalled, TEXT("%s%s.cmp"), g_szProfileSourceDir, pszShortServiceName)); iCmpVersionToInstall = GetPrivateProfileInt(c_pszCmSectionProfileFormat, c_pszVersion, 0, szCmpToBeInstalled); if (iCurrentCmpVersion > iCmpVersionToInstall) { // // We must not allow the install because a newer version of the profile format // is already installed. // if (!bSilent) { MYVERIFY(0 != LoadString(hInstance, IDS_NEWER_SAMENAME, szFmtString, CELEMS(szFmtString))); MYVERIFY(CELEMS(szMsg) > (UINT)wsprintf(szMsg, szFmtString, pszServiceName)); MessageBox(NULL, szMsg, pszServiceName, MB_OK | MB_TOPMOST | MB_SYSTEMMODAL); } return -1; } else if (iCurrentCmpVersion < c_iCM12ProfileVersion) { int iRet; // // Make sure that this isn't a Non-Admin NT5 person trying to install // if (plat->IsAtLeastNT5() && !IsAdmin()) { CMASSERTMSG(!bSilent, TEXT("NeedCM10Upgrade -- NonAdmin trying to Same Name upgrade a profile, exiting!")); if (!bSilent) { MYVERIFY(0 != LoadString(hInstance, IDS_GET_ADMIN, szFmtString, CELEMS(szFmtString))); MYVERIFY(CELEMS(szMsg) > (UINT)wsprintf(szMsg, szFmtString, pszServiceName)); MessageBox(NULL, szMsg, pszServiceName, MB_OK); } return -1; } else { // // Now prompt the user to make sure that they want to go ahead with the upgrade // if (!bSilent) { MYVERIFY(0 != LoadString(hInstance, IDS_UPGRADE_SAMENAME, szFmtString, CELEMS(szFmtString))); MYVERIFY(CELEMS(szMsg) > (UINT)wsprintf(szMsg, szFmtString, pszServiceName)); iRet = MessageBox(NULL, szMsg, pszServiceName, MB_YESNO | MB_TOPMOST | MB_SYSTEMMODAL); } else { // // Assume yes with Silent Same Name Upgrade // iRet = IDYES; } } if (IDYES == iRet) { if (UpdateCmpDataFromExistingProfile(pszShortServiceName, szCurrentCmp, szCmpToBeInstalled)) { CFileNameParts FileParts(szCurrentCmp); if (0 != GetSystemDirectory(szFmtString, MAX_PATH)) // use szFmtString as a temp { MYVERIFY(CELEMS(szMsg) > (UINT)wsprintf(szMsg, TEXT("%s\\%s.inf"), szFmtString, FileParts.m_FileName)); if (FileExists(szMsg)) { lstrcpy(pszOldInfPath, szMsg); } else { // // Not in the system directory, try profile dir. // MYVERIFY(CELEMS(szMsg) > (UINT)wsprintf(szMsg, TEXT("%s%s%s\\%s.inf"), FileParts.m_Drive, FileParts.m_Dir, FileParts.m_FileName, FileParts.m_FileName)); if (FileExists(szMsg)) { lstrcpy(pszOldInfPath, szMsg); } else { CMASSERTMSG(FALSE, TEXT("Unable to locate the profile INF -- old profile won't be uninstalled but installation will continue.")); pszOldInfPath[0] = TEXT('\0'); } } } } else { CMASSERTMSG(FALSE, TEXT("Couldn't copy cmp file for same name upgrade. Exiting.")); return -1; } return TRUE; } else { return -1; } } else { // // Then either the version numbers are the same or the version to install is newer but // the existing profile is at least a 1.2 profile. // return FALSE; } } MYVERIFY(ERROR_SUCCESS == RegCloseKey(hKey)); } return FALSE; } //+---------------------------------------------------------------------------- // // Function: MeetsMinimumProfileInstallVersion // // Synopsis: Because of problems with previous profile installers (namely 1.0), // we built in minimum install requirements for profiles. Thus we // look under the Connection Manager App Paths key for a minimum profile // version, a minimum build number, and a minimum profile format version. // If the profile trying to install doesn't meet any of these requirements, // then the function returns FALSE and the install is failed. // // Arguments: DWORD dwInstallerVersionNumber - current installer version number // DWORD dwInstallerBuildNumber - current installer build number // LPCTSTR pszInfFile - path to the inf to get the profile format version number // // Returns: BOOL - TRUE if all the version requirements are met // // History: quintinb Created Header 5/24/99 // //+---------------------------------------------------------------------------- BOOL MeetsMinimumProfileInstallVersion(DWORD dwInstallerVersionNumber, DWORD dwInstallerBuildNumber, LPCTSTR pszInfFile) { const TCHAR* const c_pszRegMinProfileVersion = TEXT("MinProfileVersion"); const TCHAR* const c_pszRegMinProfileBuildNum = TEXT("MinProfileBuildNum"); const TCHAR* const c_pszRegMinProfileFmtVersion = TEXT("MinProfileFmtVersion"); HKEY hKey; DWORD dwTemp; // // First check the format version. // if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_pszRegCmAppPaths, 0, KEY_READ, &hKey)) { DWORD dwSize = sizeof(DWORD); DWORD dwType = REG_DWORD; if (ERROR_SUCCESS == RegQueryValueEx(hKey, c_pszRegMinProfileFmtVersion, NULL, &dwType, (LPBYTE)&dwTemp, &dwSize)) { // // Get the profile format version from the cmp file // DWORD dwFormatVersion; CFileNameParts InfParts(pszInfFile); TCHAR szCmpFile[MAX_PATH+1]; MYVERIFY(CELEMS(szCmpFile) > (UINT)wsprintf(szCmpFile, TEXT("%s%s%s%s"), InfParts.m_Drive, InfParts.m_Dir, InfParts.m_FileName, c_pszCmpExt)); dwFormatVersion = (DWORD)GetPrivateProfileInt(c_pszCmSectionProfileFormat, c_pszVersion, -1, szCmpFile); if (dwTemp > dwFormatVersion) { return FALSE; } } // // Next Check the profile version (equivalent to the version number of the // CM bits the profile was built with) // if (ERROR_SUCCESS == RegQueryValueEx(hKey, c_pszRegMinProfileVersion, NULL, &dwType, (LPBYTE)&dwTemp, &dwSize)) { // // If the minimum version number from the registry is higher than the // version number listed here, fail the install. // if (dwTemp > dwInstallerVersionNumber) { return FALSE; } } // // Next Check the profile build number (equivalent to the build number of the // CM bits the profile was built with) // if (ERROR_SUCCESS == RegQueryValueEx(hKey, c_pszRegMinProfileBuildNum, NULL, &dwType, (LPBYTE)&dwTemp, &dwSize)) { // // If the minimum version number from the registry is higher than the // version number listed here, fail the install. // if (dwTemp > dwInstallerBuildNumber) { return FALSE; } } RegCloseKey(hKey); } return TRUE; } //+---------------------------------------------------------------------------- // // Function: UninstallExistingCmException // // Synopsis: This function looks for the cmexcept.inf file in the %windir%\inf // directory. If this file exists, then we uninstall the // existing exception before we install the new one. This prevents // the rollback information from being lost. // // Arguments: none // // Returns: BOOL - returns TRUE if the installer needs to uninstall the // existing CM exception install and FALSE if the install // can continue without it. // // History: quintinb Created 11/1/98 // //+---------------------------------------------------------------------------- HRESULT UninstallExistingCmException() { HRESULT hr = E_UNEXPECTED; LPCSTR c_pszCmExceptInfRelative = TEXT("\\Inf\\cmexcept.inf"); LPCSTR c_pszUnInstallSection = "DefaultUninstall_NoPrompt"; UINT uNumChars = GetWindowsDirectoryA(NULL, 0); if (uNumChars) { uNumChars = uNumChars + lstrlenA(c_pszCmExceptInfRelative); LPSTR pszPathToCmExceptInf = (LPSTR)CmMalloc(sizeof(CHAR)*(uNumChars + 1)); if (pszPathToCmExceptInf) { if (GetWindowsDirectoryA(pszPathToCmExceptInf, uNumChars)) { lstrcatA(pszPathToCmExceptInf, c_pszCmExceptInfRelative); if (FileExists(pszPathToCmExceptInf)) { // // We have an exception inf in the directory so we need to uninstall it. Were the // bits already on the machine newer than the bits we have in the cab, then we wouldn't // be installing. If the bits on the machine don't match the version that the inf says, // then we are better uninstalling those bits and putting them in a known state anyway. // hr = CallLaunchInfSectionEx(pszPathToCmExceptInf, c_pszUnInstallSection, ALINF_ROLLBKDOALL); CMTRACE1(TEXT("UninstallExistingCmException -- CM Exception inf found, uninstalling. CallLaunchInfSectionEx returned hr=0x%x"), hr); } else { hr = S_FALSE; // nothing to delete } } CmFree(pszPathToCmExceptInf); } else { hr = E_OUTOFMEMORY; } } return hr; } //+---------------------------------------------------------------------------- // // Function: CheckCmAndIeRequirements // // Synopsis: This function checks the CM and IE requirements for a profile // and returns whether the CM should be installed, whether profile // migration should occur, and most importantly if the install should // exit now because of insufficient requirements. // // Arguments: BOOL* pbInstallCm - tells if CM should be installed or not // BOOL* pbMigrateExistingProfiles - tells if profile migration should occur // LPCTSTR szInfFile - the inf file to install // LPCTSTR szServiceName - The Service name, used as a title // // Returns: BOOL - returns TRUE if the install should continue, FALSE if // if the install should be failed. // // History: quintinb Created 11/1/98 // //+---------------------------------------------------------------------------- BOOL CheckCmAndIeRequirements(HINSTANCE hInstance, BOOL* pbInstallCm, BOOL* pbMigrateExistingProfiles, LPCTSTR szInfFile, BOOL bNoSupportFiles, LPCTSTR szServiceName, BOOL bSilent) { CmVersion CmVer; CPlatform plat; BOOL bReturn; BOOL bCMRequired; TCHAR szMsg[2*MAX_PATH+1]; TCHAR szTemp[MAX_PATH+1]; TCHAR szString[MAX_PATH+1]; DWORD dwInstallerBuildNumber = 0; DWORD dwInstallerVersionNumber = 0; // // The inf file tells us if we included the CM bits // if (plat.IsNT5()) { // // We now need to check to see if we need to install the Windows XP bits on // Windows 2000. Thus we check the inf to see if this profile includes the CM // bits or not. Note that we never want to install the IE support files on // Win2k so set bIERequired to TRUE. // bCMRequired = !GetPrivateProfileInt(c_pszCmakStatus, TEXT("IncludeCMCode"), 0, szInfFile); } else if (CmIsNative()) { // // CM and IE are required on Windows XP and any platforms with the Native // regkey set except NT5 and Win98 SE as they are special cases. // bCMRequired = TRUE; } else { bCMRequired = !GetPrivateProfileInt(c_pszCmakStatus, TEXT("IncludeCMCode"), 0, szInfFile); } // // Now try to get the version numbers from the profile INF // dwInstallerBuildNumber = (DWORD)GetPrivateProfileInt(c_pszSectionCmDial32, c_pszVerBuild, ((VER_PRODUCTBUILD << c_iShiftAmount) + VER_PRODUCTBUILD_QFE), szInfFile); dwInstallerVersionNumber = (DWORD)GetPrivateProfileInt(c_pszSectionCmDial32, c_pszVersion, (HIBYTE(VER_PRODUCTVERSION_W) << c_iShiftAmount) + (LOBYTE(VER_PRODUCTVERSION_W)), szInfFile); // // First check to see if we have any install minimums in the registry. If these // minimums exist and our profile doesn't meet those minimums then we must // throw an error message and exit. // if (!MeetsMinimumProfileInstallVersion(dwInstallerVersionNumber, dwInstallerBuildNumber, szInfFile)) { if (!bSilent) { MYVERIFY(0 != LoadString(hInstance, IDS_PROFILE_TOO_OLD, szMsg, MAX_PATH)); MessageBox(NULL, szMsg, szServiceName, MB_OK | MB_ICONERROR); } return FALSE; } // // Should we migrate existing profiles? Always try to migrate if we find all user // profiles already on the machine. // *pbMigrateExistingProfiles = AllUserProfilesInstalled(); // // Do CM bits exist on the machine? // if (CmVer.IsPresent()) { if ((dwInstallerVersionNumber < CmVer.GetVersionNumber()) || (dwInstallerBuildNumber < CmVer.GetBuildAndQfeNumber())) { // // If the CM bits on the machine are newer than the bits we have in the cab, // then we only want to install the profile files and not the CM bits themselves. // *pbInstallCm = FALSE; bReturn = TRUE; } else { // // Then the CM bits on the machine are older than the bits in the cab or // the two versions match. Either way, we should install the bits in the // cab unless this is Win2k where we never want to re-install the same // version of CM bits as we will lose our rollback information. // if (bCMRequired) { if ((dwInstallerVersionNumber == CmVer.GetVersionNumber()) && (CmVer.GetBuildNumber() > c_CmMin13Version)) { // // Then the builds have the same major and minor version number // and should be considered in the same "Version Family". Note // that we also check for a minimum build number because 7.00 is // the version number for the CM 1.0 release in NT4 SP4 and we want CM // profiles to not install on NT5 Beta2 Bits. // *pbInstallCm = FALSE; bReturn = TRUE; } else { MYVERIFY(CELEMS(szString) > (UINT)wsprintf(szString, TEXT("%u.%u.%u.%u"), HIWORD(dwInstallerVersionNumber), LOWORD(dwInstallerVersionNumber), HIWORD(dwInstallerBuildNumber), LOWORD(dwInstallerBuildNumber))); if (!bSilent) { MYVERIFY(0 != LoadString(hInstance, IDS_CM_OLDVERSION, szTemp, MAX_PATH)); MYVERIFY(CELEMS(szMsg) > (UINT)wsprintf(szMsg, szTemp, szString)); MessageBox(NULL, szMsg, szServiceName, MB_OK); } return FALSE; } } else { if ((dwInstallerVersionNumber == CmVer.GetVersionNumber()) && (dwInstallerBuildNumber == CmVer.GetBuildAndQfeNumber())) { // // Don't reinstall the CM bits if they are the same version // and we are on Win2k. Doing so will overwrite the version of // CM ready for rollback. // *pbInstallCm = !(plat.IsNT5()); bReturn = TRUE; } else if (plat.IsNT5() && (FALSE == IsAdmin())) { // // If this is Win2k and we need to install the CM binaries via the exception installer, // then the user must be an Administrator to do so. Since this user isn't, fail // the install and give the user a warning message. // if (!bSilent) { MYVERIFY(0 != LoadString(hInstance, IDS_CANNOT_INSTALL_CM, szMsg, 2*MAX_PATH)); MessageBox(NULL, szMsg, szServiceName, MB_OK | MB_ICONEXCLAMATION); } return FALSE; } else { // // If this is Win2k, we need to make sure we aren't doing a cross language install. // Basically, we want to ensure that we aren't installing English CM bits on a native // German machine for instance. If so, fail the install and inform the user why. // if (plat.IsNT5()) { const TCHAR* const c_pszCmstp = TEXT("cmstp.exe"); CFileNameParts InfFileParts(szInfFile); DWORD dwLen = lstrlen(InfFileParts.m_Drive) + lstrlen(InfFileParts.m_Dir) + lstrlen(c_pszCmstp); if (MAX_PATH >= dwLen) { wsprintf(szTemp, TEXT("%s%s%s"), InfFileParts.m_Drive, InfFileParts.m_Dir, c_pszCmstp); CVersion CmstpVer(szTemp); DWORD dwExistingCmLcid = CmVer.GetLCID(); DWORD dwCmstpLcid = CmstpVer.GetLCID(); if (FALSE == ArePrimaryLangIDsEqual(dwExistingCmLcid, dwCmstpLcid)) { if (!bSilent) { MYVERIFY(0 != LoadString(hInstance, IDS_CROSS_LANG_INSTALL, szMsg, 2*MAX_PATH)); MessageBox(NULL, szMsg, szServiceName, MB_OK | MB_ICONEXCLAMATION); } return FALSE; } } } // // Now check to see if installing CM is going to have an effect on CMAK // CmakVersion CmakVer; if (CmakVer.Is121Cmak() || CmakVer.Is122Cmak()) { // // Then the Win2k or IEAK5 version of CMAK is installed. Installing // the Whistler version of CM will break this version of CMAK. We // need to ask the user if they wish to continue the install and break // CMAK or abort the install and leave it as is. // if (bSilent) { return FALSE; } else { MYVERIFY(0 != LoadString(hInstance, IDS_INSTCM_WITH_OLD_CMAK, szMsg, 2*MAX_PATH)); if (IDNO == MessageBox(NULL, szMsg, szServiceName, MB_YESNO | MB_DEFBUTTON2 | MB_ICONEXCLAMATION)) { return FALSE; } } } *pbInstallCm = TRUE; bReturn = TRUE; } } } } else { if (bCMRequired) { // // This is an error because we need CM bits but don't have any on // the machine or in the cab (or its Whistler and we won't install them). // if (!bSilent) { MYVERIFY(0 != LoadString(hInstance, IDS_CM_NOTPRESENT, szMsg, MAX_PATH)); MessageBox(NULL, szMsg, szServiceName, MB_OK); } return FALSE; } else { MYDBGASSERT(FALSE == plat.IsNT5()); // we shouldn't be in this state on Win2k but it is probably // better for the user if we install. *pbInstallCm = TRUE; bReturn = TRUE; } } if (!bNoSupportFiles) { if (!CheckIeDllRequirements(&plat)) { if (!bSilent) { MYVERIFY(0 != LoadString(hInstance, IDS_NO_SUPPORTFILES, szMsg, MAX_PATH)); MessageBox(NULL, szMsg, szServiceName, MB_OK); } return FALSE; } } return bReturn; } //+---------------------------------------------------------------------------- // // Function: GetInstallOptions // // Synopsis: This function decides if the profile should be installed for all // users or the current user only, as well as whether the user prefers // a desktop icon, a start menu link, both, or neither. // // Arguments: OUT BOOL* pbInstallForAllUsers - should the profile be installed for all users // OUT BOOL* pbCreateDesktopIcon - should a desktop icon be created (if NT5) // IN BOOL bCM10Upgrade - is this profile upgrading an older same name profile // IN BOOL bNoNT5Shortcut - whether the user specified a switch saying they didn't want an NT5 Shortcut // IN BOOL bSilentSingleUser - whether the user specified a switch saying they wanted a silent Single User install // IN BOOL bSilentAllUser - whether the user specified a switch saying they wanted a silent ALL User install // // Returns: TRUE if the install should continue, FALSE otherwise // // History: quintinb Created 11/1/98 // //+---------------------------------------------------------------------------- BOOL GetInstallOptions(HINSTANCE hInstance, BOOL* pbInstallForAllUsers, BOOL* pbCreateDesktopIcon, BOOL bCM10Upgrade, BOOL bNoNT5Shortcut, BOOL bSingleUser, BOOL bSilent, LPTSTR pszServiceName) { // // We will only allow NT5 users who are administrators to have a choice of how // the profile is installed. If the user is on a legacy platform then the profile // will be installed for all users just as before. If the profile is installed by // an NT5 user that is not an admin, it will be installed just for them. If they // are an admin then they can choose if they want the profile available to all users // or just for themselves. If we are on NT5 we also allow the user to choose if // they want a Desktop Shortcut or not. // INT_PTR iUiReturn; CPlatform plat; if (plat.IsWin9x() || plat.IsNT4()) { // // Legacy install, force to all users (ignore SingleUser flag because not supported). // *pbInstallForAllUsers = TRUE; } else { int iDialogID; if (bSilent) { *pbCreateDesktopIcon = !bNoNT5Shortcut; if (IsAdmin() && !bSingleUser) { *pbInstallForAllUsers = TRUE; } else { *pbInstallForAllUsers = FALSE; } } else { if (IsAdmin()) { // // The user is a local admin, we need to prompt to see if they want to install // the profile for themselves or for all users. However, if we are doing a // same name upgrade, then we always do an all users install and don't give the // admin any choice. // if (bCM10Upgrade) { iDialogID = IDD_NOCHOICEUI; } else { iDialogID = IDD_ADMINUI; } } else { // // Just a normal user, but we still need to prompt for whether they want // a desktop shortcut // if (bCM10Upgrade) { CMASSERTMSG(FALSE, TEXT("Non-Admin NT5 made it to UI choice section. Check CM 1.0 upgrade code.")); return FALSE; } else { iDialogID = IDD_NOCHOICEUI; } } InitDialogStruct DialogArgs; DialogArgs.pszTitle = pszServiceName; DialogArgs.bNoDesktopIcon = bNoNT5Shortcut; DialogArgs.bSingleUser = bSingleUser; iUiReturn = DialogBoxParam(hInstance, MAKEINTRESOURCE(iDialogID), NULL, (DLGPROC)ProcessPreferencesUI, (LPARAM)&DialogArgs); if (-1 == iUiReturn) { // then we had an error or the user hit cancel. Either way bail. return FALSE; } else { *pbInstallForAllUsers = (BOOL)(iUiReturn & ALLUSERS) || bCM10Upgrade; *pbCreateDesktopIcon = (BOOL)(iUiReturn & CREATEDESKTOPICON); } } } return TRUE; } BOOL VerifyProfileOverWriteIfExists(HINSTANCE hInstance, LPCTSTR pszCmsFile, LPCTSTR pszServiceName, LPCTSTR pszShortServiceName, LPTSTR pszOldInfPath, BOOL bSilent) { TCHAR szTmpServiceName [MAX_PATH+1] = TEXT(""); TCHAR szDisplayMsg[3*MAX_PATH+1] = TEXT(""); TCHAR szTemp [2*MAX_PATH+1] = TEXT(""); int iRet; if (FileExists(pszCmsFile)) { // // If the file exists then we want to make sure that the service name is the same. // If the Long Service Names are the same then we have a re-install. // If they aren't the same then we need to prompt the user and find out whether to // abandon the install or continue and overwrite it. // MYVERIFY(0 != GetPrivateProfileString(c_pszCmSection, c_pszCmEntryServiceName, TEXT(""), szTmpServiceName, CELEMS(szTmpServiceName), pszCmsFile)); if (0 != lstrcmp(szTmpServiceName, pszServiceName)) { // // If the install is silent, we will assume they know what they are doing // and we will overwrite. Otherwise prompt the user to see what they want // us to do. // if (!bSilent) { MYVERIFY(0 != LoadString(hInstance, IDS_SAME_SS_DIFF_LS, szTemp, 2*MAX_PATH)); MYVERIFY(CELEMS(szDisplayMsg) > (UINT)wsprintf(szDisplayMsg, szTemp, pszServiceName, szTmpServiceName, pszShortServiceName)); MessageBox(NULL, szDisplayMsg, pszServiceName, MB_OK | MB_TOPMOST | MB_SYSTEMMODAL); } return FALSE; } } return TRUE; } //+---------------------------------------------------------------------------- // // Function: PresharedKeyPINDlgProc // // Synopsis: This function obtains the PIN to be used for the Pre-Shared key // // History: SumitC 29-Mar-2001 Created // //+---------------------------------------------------------------------------- BOOL APIENTRY PresharedKeyPINDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static PresharedKeyPINStruct * pPSKArgs; switch (message) { case WM_INITDIALOG: pPSKArgs = (PresharedKeyPINStruct*)lParam; break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: MYDBGASSERT(pPSKArgs); if (pPSKArgs && pPSKArgs->szPIN) { GetDlgItemText(hDlg, IDC_PSK_PIN, pPSKArgs->szPIN, c_dwMaxPresharedKeyPIN); } MYVERIFY(FALSE != EndDialog(hDlg, 1)); return TRUE; case IDCANCEL: MYVERIFY(FALSE != EndDialog(hDlg, -1)); return TRUE; default: break; } break; case WM_CLOSE: MYVERIFY(FALSE != EndDialog(hDlg, -1)); return TRUE; default: return FALSE; } return FALSE; } //+---------------------------------------------------------------------------- // // Function: GetPINforPresharedKey // // Synopsis: Asks the user for a PIN (to be used to decrypt the pre-shared key) // // Arguments: [hInstance] - used for bringing up dialogs // [ppszPIN] - ptr to where to put Pre-Shared Key PIN // // Returns: LRESULT (ERROR_SUCCESS if we got a PIN, // ERROR_INVALID_DATA if user cancelled out of PIN dialog, // ERROR_INVALID_PARAMETER if params are bad (this is a coding issue) // // History: 3-Apr-2001 SumitC Created // //----------------------------------------------------------------------------- LRESULT GetPINforPresharedKey(HINSTANCE hInstance, LPTSTR * ppszPIN) { LRESULT lRet = ERROR_SUCCESS; MYDBGASSERT(hInstance); MYDBGASSERT(ppszPIN); if (NULL == hInstance || NULL == ppszPIN) { return ERROR_INVALID_PARAMETER; } *ppszPIN = NULL; // // Get the PIN // PresharedKeyPINStruct PresharedKeyPINArgs = {0}; INT_PTR iUiReturn = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_PRESHAREDKEY_PIN), NULL, (DLGPROC)PresharedKeyPINDlgProc, (LPARAM)&PresharedKeyPINArgs); if (-1 == iUiReturn) { lRet = ERROR_INVALID_DATA; // caller maps to appropriate error message. } else { DWORD dwLen = lstrlen(PresharedKeyPINArgs.szPIN); if (0 == dwLen) { lRet = ERROR_INVALID_DATA; // caller maps to appropriate error message. } else { *ppszPIN = (LPTSTR) CmMalloc((dwLen + 1) * sizeof(TCHAR)); if (*ppszPIN) { lstrcpy(*ppszPIN, PresharedKeyPINArgs.szPIN); } } } return lRet; } //+---------------------------------------------------------------------------- // // Function: DecryptPresharedKeyUsingPIN // // Synopsis: Given an encoded preshared key and a PIN to be used for decoding, // performs the decoding job. // // Arguments: [pszEncodedPresharedKey] - encoded Preshared key // [pszPreSharedKeyPIN] - the PIN // [ppszPreSharedKey] - ptr to buffer to place Pre-Shared Key // // Returns: LRESULT (ERROR_SUCCESS successfully decoded // ERROR_INVALID_PARAMETER if params are bad (this is a coding issue) // other errors as encountered while calling crypto APIs // // History: 3-Apr-2001 SumitC Created // //----------------------------------------------------------------------------- LRESULT DecryptPresharedKeyUsingPIN(LPCTSTR pszEncodedPresharedKey, LPCTSTR pszPresharedKeyPIN, LPTSTR * ppszPresharedKey) { LRESULT lRet = ERROR_BADKEY; if (lstrlen(pszPresharedKeyPIN) < c_dwMinPresharedKeyPIN) { CMTRACE(TEXT("DecryptPresharedKeyUsingPIN - PIN is too short")); return lRet; } if (lstrlen(pszPresharedKeyPIN) > c_dwMaxPresharedKeyPIN) { CMTRACE(TEXT("DecryptPresharedKeyUsingPIN - PIN is too long")); return lRet; } // // Init Cmsecure // InitSecure(FALSE); // use secure, not fast encryption // // decrypt to get Preshared key // if (pszEncodedPresharedKey && pszPresharedKeyPIN) { DWORD dwLen = 0; if (FALSE == DecryptString((LPBYTE)pszEncodedPresharedKey, lstrlen(pszEncodedPresharedKey) * sizeof(TCHAR), (LPSTR)pszPresharedKeyPIN, (LPBYTE *)ppszPresharedKey, &dwLen, (PFN_CMSECUREALLOC)CmMalloc, (PFN_CMSECUREFREE)CmFree)) { CMTRACE1(TEXT("DecryptPresharedKeyUsingPIN - DecryptString failed with %d"), GetLastError()); lRet = ERROR_BADKEY; } else { lRet = ERROR_SUCCESS; CMASSERTMSG(dwLen, TEXT("DecryptString succeeded, but pre-shared key retrieved was 0 bytes?")); } } // // Deinit cmsecure // DeInitSecure(); return lRet; } //+---------------------------------------------------------------------------- // // Function: SetThisConnectionAsDefault // // Synopsis: This function loads inetcfg.dll and calls the InetSetAutodial // entry on the given service name. Thus this function sets the // given servicename as the IE default connection. // // Arguments: LPCSTR pszServiceName - Long service name of the connection to set // // Returns: BOOL - TRUE if successful // // History: quintinb Created 03/04/00 // //+---------------------------------------------------------------------------- BOOL SetThisConnectionAsDefault(LPSTR pszServiceName) { BOOL bReturn = FALSE; typedef HRESULT (WINAPI *pfnInetSetAutodialProc)(BOOL, LPSTR); if (pszServiceName && (TEXT('\0') != pszServiceName[0])) { CDynamicLibrary CnetCfg; if (CnetCfg.Load(TEXT("inetcfg.dll"))) { pfnInetSetAutodialProc pfnInetSetAutodial = (pfnInetSetAutodialProc)CnetCfg.GetProcAddress("InetSetAutodial"); if (pfnInetSetAutodial) { HRESULT hr = pfnInetSetAutodial(TRUE, pszServiceName); // TRUE == fEnable bReturn = SUCCEEDED(hr); } } } return bReturn; } //+---------------------------------------------------------------------------- // // Function: InstallInf // // Synopsis: This is the driver code for installing a CM profile. // // Arguments: HINSTANCE hInstance - Instance handle for resources // LPCTSTR szInfFile - INF file to install // BOOL bNoSupportFiles - forces browser files not to be installed. // BOOL bNoLegacyIcon - Don't install with a legacy Icon // BOOL bNoNT5Shortcut - Don't give the user a NT5 Desktop Shortcut // BOOL bSilentSingleUser - Install the profile silently for a single user (NT5 only) // BOOL bSilentAllUser - Install the profile for All Users silently // BOOL bSetAsDefault - set as the default connection once installed // CNamedMutex* pCmstpMutex - pointer to the cmstp mutex object so // that it can be released once the profile is launched // // Returns: HRESULT - standard COM error codes // // History: quintinb Created 7/14/98 // quintinb added support for new switches (252872) 11/20/98 // //+---------------------------------------------------------------------------- HRESULT InstallInf(HINSTANCE hInstance, LPCTSTR szInfFile, BOOL bNoSupportFiles, BOOL bNoLegacyIcon, BOOL bNoNT5Shortcut, BOOL bSilent, BOOL bSingleUser, BOOL bSetAsDefault, CNamedMutex* pCmstpMutex) { CPlatform plat; BOOL bMigrateExistingProfiles; BOOL bInstallCm; BOOL bMustReboot = FALSE; BOOL bCM10Upgrade = FALSE; HRESULT hrReturn = S_OK; HRESULT hrTemp = S_OK; BOOL bInstallForAllUsers; BOOL bCreateDesktopIcon = FALSE; HKEY hKey; DWORD dwSize; DWORD dwType; TCHAR szInstallDir[MAX_PATH+1]; TCHAR szTemp[2*MAX_PATH+1]; TCHAR szCmsFile[MAX_PATH+1]; TCHAR szOldInfPath[MAX_PATH+1]; TCHAR szServiceName[MAX_PATH+1]; TCHAR szShortServiceName[MAX_PATH+1]; TCHAR szTitle[MAX_PATH+1]; LPTSTR pszPhonebook = NULL; LPTSTR pszCmpFile = NULL; LPTSTR pszPresharedKey = NULL; //CMASSERTMSG(FALSE, TEXT("Attach the Debugger now!")); MYDBGASSERT((szInfFile) && (TEXT('\0') != szInfFile[0])); CFileNameParts InfParts(szInfFile); wsprintf(g_szProfileSourceDir, TEXT("%s%s"), InfParts.m_Drive, InfParts.m_Dir); MYVERIFY(0 != LoadString(hInstance, IDS_CMSTP_TITLE, szTitle, CELEMS(szTitle))); MYDBGASSERT(TEXT('\0') != szTitle[0]); // // Get the ServiceName and ShortServicename from the inf file // MYVERIFY(0 != GetPrivateProfileString(c_pszInfSectionStrings, c_pszCmEntryServiceName, TEXT(""), szServiceName, CELEMS(szServiceName), szInfFile)); MYVERIFY(0 != GetPrivateProfileString(c_pszInfSectionStrings, c_pszShortSvcName, TEXT(""), szShortServiceName, CELEMS(szShortServiceName), szInfFile)); if ((TEXT('\0') == szServiceName[0]) || (TEXT('\0') == szShortServiceName[0])) { CMASSERTMSG(FALSE, TEXT("Either the ServiceName or the ShortServiceName are empty, exiting.")); hrReturn = E_FAIL; goto exit; } // // If this is NT5, check the New Connection Wizard Policy to see if the user is allowed to // create new connections. If not, then don't let them install. // if (plat.IsAtLeastNT5()) { LPTSTR c_pszNewPolicy = TEXT("NC_NewConnectionWizard"); LPTSTR c_pszConnectionsPoliciesKey = TEXT("Software\\Policies\\Microsoft\\Windows\\Network Connections"); HKEY hKey = NULL; // // Administrators and all Authenticated users have access to install profiles // by default. Non-Authenticated users don't have access to install profiles // because they don't have permission to start Rasman. Thus, even if we // allowed them to try to install, it would fail when we couldn't create a // connectoid for the profile. // DWORD dwAllowedToInstall = IsAuthenticatedUser() || IsAdmin(); // // Now we need to check the policy registry key to see if someone has overriden // the default behavior. If so, then we will honor it by setting dwAllowedToInstall // to the value of the policy key. Note that we even check the registry key for // authenticated users (an Admin could enable installation for all users, but users // 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 // connections. However the policy may be setup so that they cannot. Lets assume they // can and then check the regkey. // if (dwAllowedToInstall) { LONG lResult = RegOpenKeyEx(HKEY_CURRENT_USER, c_pszConnectionsPoliciesKey, 0, KEY_READ, &hKey); if (ERROR_SUCCESS == lResult) { dwSize = sizeof(dwAllowedToInstall); lResult = RegQueryValueEx(hKey, c_pszNewPolicy, NULL, NULL, (LPBYTE)&dwAllowedToInstall, &dwSize); RegCloseKey(hKey); } } if (!dwAllowedToInstall) { // // The user isn't allowed to create new connections, thus they aren't allowed to install // CM connections. Throw an error message about permissions and exit. // MYVERIFY(0 != LoadString(hInstance, IDS_INSTALL_NOT_ALLOWED, szTemp, CELEMS(szTemp))); MessageBox(NULL, szTemp, szServiceName, MB_OK); hrReturn = E_ACCESSDENIED; goto exit; } } if (!CheckCmAndIeRequirements(hInstance, &bInstallCm, &bMigrateExistingProfiles, szInfFile, bNoSupportFiles, szServiceName, bSilent)) { hrReturn = E_FAIL; goto exit; } // // Check to see if we have a same name upgrade // bCM10Upgrade = NeedCM10Upgrade(hInstance, szServiceName, szShortServiceName, szOldInfPath, bSilent, &plat); if (-1 == bCM10Upgrade) { // // if NeedCM10Upgrade returned -1 then either an error occurred or // the user decided not to upgrade. Either way, bail. // hrReturn = S_FALSE; goto exit; } // // Check to see if a Pre-shared Key is present, and require a PIN if so // pszCmpFile = szTemp; // re-use szTemp to save stack space and not get into trouble on Win9x MYVERIFY(CELEMS(szTemp) > (UINT)wsprintf(pszCmpFile, TEXT("%s%s.cmp"), g_szProfileSourceDir, szShortServiceName)); if (FileExists(pszCmpFile)) { pszPresharedKey = GetPrivateProfileStringWithAlloc(c_pszCmSection, c_pszCmEntryPresharedKey, TEXT(""), pszCmpFile); if (pszPresharedKey && (0 != lstrcmp(pszPresharedKey, TEXT("")))) { CMTRACE(TEXT("Got a pre-shared key")); if (FALSE == plat.IsAtLeastNT51()) { // NOTE: pszCmpFile is really szTemp, and we're about to overwrite it, but // it's ok because we're also about to exit MYVERIFY(0 != LoadString(hInstance, IDS_PSK_NEEDS_XP, szTemp, CELEMS(szTemp))); MessageBox(NULL, szTemp, szServiceName, MB_OK | MB_ICONERROR); hrReturn = S_FALSE; goto exit; } BOOL bEncrypted = (BOOL) GetPrivateProfileInt(c_pszCmSection, c_pszCmEntryKeyIsEncrypted, FALSE, pszCmpFile); if (bEncrypted) { CMTRACE(TEXT("Pre-shared key is encrypted")); LPTSTR pszPresharedKeyPIN = NULL; LRESULT lRet = GetPINforPresharedKey(hInstance, &pszPresharedKeyPIN); if ((ERROR_SUCCESS == lRet) && pszPresharedKeyPIN) { // // The Pre-shared key is encoded // LPTSTR pszPresharedKeyDecoded = NULL; lRet = DecryptPresharedKeyUsingPIN(pszPresharedKey, pszPresharedKeyPIN, &pszPresharedKeyDecoded); CmFree(pszPresharedKey); if (ERROR_SUCCESS == lRet) { pszPresharedKey = pszPresharedKeyDecoded; } else { pszPresharedKey = NULL; lRet = ERROR_BADKEY; } } CmFree(pszPresharedKeyPIN); if (ERROR_SUCCESS != lRet) { switch (lRet) { case ERROR_INVALID_DATA: MYVERIFY(0 != LoadString(hInstance, IDS_PSK_GOTTA_HAVE_IT, szTemp, CELEMS(szTemp))); break; case ERROR_BADKEY: MYVERIFY(0 != LoadString(hInstance, IDS_PSK_INCORRECT_PIN, szTemp, CELEMS(szTemp))); break; default: MYVERIFY(0 != LoadString(hInstance, IDS_UNEXPECTEDERR, szTemp, CELEMS(szTemp))); MYDBGASSERT(0); break; } MessageBox(NULL, szTemp, szServiceName, MB_OK | MB_ICONEXCLAMATION); hrReturn = E_ACCESSDENIED; goto exit; } } } } if (!GetInstallOptions(hInstance, &bInstallForAllUsers, &bCreateDesktopIcon, bCM10Upgrade, bNoNT5Shortcut, bSingleUser, bSilent, szServiceName)) { hrReturn = S_FALSE; goto exit; } // // Get the installation path // ZeroMemory(szInstallDir, sizeof(szInstallDir)); if (bInstallForAllUsers) { // // Install for All Users // if (!GetAllUsersCmDir(szInstallDir, hInstance)) { hrReturn = E_FAIL; goto exit; } } else { // // Install only for the current user // GetPrivateCmUserDir(szInstallDir, hInstance); //lint !e534 if (TEXT('\0') == szInstallDir[0]) { hrReturn = E_FAIL; goto exit; } } MYVERIFY(CELEMS(szCmsFile) > (UINT)wsprintf(szCmsFile, TEXT("%s\\%s\\%s.cms"), szInstallDir, szShortServiceName, szShortServiceName)); // // Check for two profiles with the same Short Service Name and different Long Service // Names // if (!VerifyProfileOverWriteIfExists(hInstance, szCmsFile, szServiceName, szShortServiceName, szOldInfPath, bSilent)) { hrReturn = S_FALSE; goto exit; } // Now Migrate users old cm profiles (to have full paths to their CMP files in the // desktop GUID) if necessary // if (bMigrateExistingProfiles) { // // Ignore the return here for now. Not much we can do about it at this stage. // Should we give them an error? // MYVERIFY(SUCCEEDED(MigrateOldCmProfilesForProfileInstall(hInstance, g_szProfileSourceDir))); } if (bCM10Upgrade) { // // Uninstall the current profile so that we can install the newer version. Note // that we don't want to use the uninstall string because it might call for // cmstp.exe which is already running. Thus uninstall by calling UninstallProfile // directly. Note that we do not delete the credentials on a same name upgrade // profile uninstall. // if (szOldInfPath[0]) { RemoveShowIconFromRunPostSetupCommands(szOldInfPath); MYVERIFY(SUCCEEDED(UninstallProfile(hInstance, szOldInfPath, FALSE))); // bCleanUpCreds == FALSE } } else { // // We need to check if we are installing over another profile of the same name. // If so, then we want to recover the cmp data unless this is a CM 1.0 upgrade // in which case we have already done this as part of that upgrade code. // if (-1 == MigrateCmpData(hInstance, bInstallForAllUsers, szServiceName, szShortServiceName, bSilent)) { hrReturn = S_FALSE; goto exit; } } // // In order to keep MSN's online setup working we need to keep the all user install // registry key (used to communicate the path to the inf) in the same place that it was // for the Win98 SE/Beta3 release. The Single user reg key location had to be moved to // allow plain old users to install profiles. // HKEY hBaseKey; LPTSTR pszRegInfCommKeyPath; if (bInstallForAllUsers) { hBaseKey = HKEY_LOCAL_MACHINE; pszRegInfCommKeyPath = (LPTSTR)c_pszRegCmAppPaths; } else { hBaseKey = HKEY_CURRENT_USER; pszRegInfCommKeyPath = (LPTSTR)c_pszRegCmRoot; } // // Now create the install dir and the reg key to communicate this info to the inf file. // if (TEXT('\0') != szInstallDir[0]) { // // Create the full path to the installation directory. // MYVERIFY(FALSE != CreateLayerDirectory(szInstallDir)); // // Create the Profile subdirectory too, that way we avoid profile // install problems on Win98 -- NTRAID 376878 // MYVERIFY(CELEMS(szTemp) > (UINT)wsprintf(szTemp, TEXT("%s\\%s"), szInstallDir, szShortServiceName)); MYVERIFY(FALSE != CreateLayerDirectory(szTemp)); // // We now need to write the registry key that the inf will use as the // installation directory. See the CustomDestination section of the // profile inf to see where this ties in. // if (plat.IsWin9x()) { // // Then we need to use the Short Name in the regkey or the inf will not install properly // MYVERIFY(0 != GetShortPathName(szInstallDir, szTemp, CELEMS(szTemp))); lstrcpy(szInstallDir, szTemp); } if (ERROR_SUCCESS != RegCreateKey(hBaseKey, pszRegInfCommKeyPath, &hKey)) { CMASSERTMSG(FALSE, TEXT("InstallInf -- Unable to create the Inf Communication Key")); MYVERIFY(0 != LoadString(hInstance, IDS_UNEXPECTEDERR, szTemp, CELEMS(szTemp))); MessageBox(NULL, szTemp, szServiceName, MB_OK); hrReturn = E_FAIL; goto exit; } // // We now need to create the value with our szInstallDir string. // dwType = REG_SZ; dwSize = lstrlen(szInstallDir); if (ERROR_SUCCESS != RegSetValueEx(hKey, c_pszProfileInstallPath, NULL, dwType, (CONST BYTE *)szInstallDir, dwSize)) { CMASSERTMSG(FALSE, TEXT("InstallInf -- Unable to set the Profile Install Path value.")); MYVERIFY(0 != LoadString(hInstance, IDS_UNEXPECTEDERR, szTemp, CELEMS(szTemp))); MessageBox(NULL, szTemp, szServiceName, MB_OK); MYVERIFY(ERROR_SUCCESS == RegCloseKey(hKey)); hrReturn = E_FAIL; goto exit; } MYVERIFY(ERROR_SUCCESS == RegCloseKey(hKey)); } else { CMASSERTMSG(FALSE, TEXT("InstallInf -- Unable to resolve the Install Directory.")); MYVERIFY(0 != LoadString(hInstance, IDS_UNEXPECTEDERR, szTemp, CELEMS(szTemp))); MessageBox(NULL, szTemp, szServiceName, MB_OK); hrReturn = E_FAIL; goto exit; } // // Install the Profile Files and create the mappings entry // if (bInstallForAllUsers) { hrTemp = LaunchInfSection(szInfFile, TEXT("DefaultInstall"), szTitle, bSilent); MYDBGASSERT(SUCCEEDED(hrTemp)); bMustReboot = (ERROR_SUCCESS_REBOOT_REQUIRED == hrTemp) ? TRUE: bMustReboot; // // Still launch this for Legacy (read MSN online setup reasons, perhaps others) // hrTemp = LaunchInfSection(szInfFile, TEXT("Xnstall_AllUser"), szTitle, bSilent); MYDBGASSERT(SUCCEEDED(hrTemp)); bMustReboot = (ERROR_SUCCESS_REBOOT_REQUIRED == hrTemp) ? TRUE: bMustReboot; } else { hrTemp = LaunchInfSection(szInfFile, TEXT("DefaultInstall_SingleUser"), szTitle, bSilent); MYDBGASSERT(SUCCEEDED(hrTemp)); bMustReboot = (ERROR_SUCCESS_REBOOT_REQUIRED == hrTemp) ? TRUE: bMustReboot; // // Still launch this for Legacy (I doubt anyone is using this but kept // for consistency with All User which at least MSN was using) // hrTemp = LaunchInfSection(szInfFile, TEXT("Xnstall_Private"), szTitle, bSilent); MYDBGASSERT(SUCCEEDED(hrTemp)); bMustReboot = (ERROR_SUCCESS_REBOOT_REQUIRED == hrTemp) ? TRUE: bMustReboot; // // Write the single user mappings key in code since parsing is involved. // if (!WriteSingleUserProfileMappings(szInstallDir, szShortServiceName, szServiceName)) { CMASSERTMSG(FALSE, TEXT("InstallInf -- WriteSingleUserProfileMappings Failed.")); MYVERIFY(0 != LoadString(hInstance, IDS_UNEXPECTEDERR, szTemp, CELEMS(szTemp))); MessageBox(NULL, szTemp, szServiceName, MB_OK); hrReturn = E_FAIL; goto exit; } } // // Install the CM bits as necessary // if (bInstallCm) { MYDBGASSERT(FALSE == plat.IsNT51()); // // First, we must extract the CM binaries from the binaries // executable/cab to the cmbins sub dir. // wsprintf(szTemp, TEXT("%scmbins\\"), g_szProfileSourceDir); hrTemp = ExtractCmBinsFromExe(g_szProfileSourceDir, szTemp); if (SUCCEEDED(hrTemp)) { if (plat.IsNT5()) { // // Check to see if we need to uninstall a previous CM exception inf // and uninstall it as necessary. // hrTemp = UninstallExistingCmException(); MYDBGASSERT((S_OK == hrTemp) || (S_FALSE == hrTemp)); // // Finally, install the CM bits // hrTemp = InstallWhistlerCmOnWin2k(szTemp); if (FAILED(hrTemp)) { if (!bSilent) { MYVERIFY(0 != LoadString(hInstance, IDS_WIN2K_CM_INSTALL_FAILED, szTemp, CELEMS(szTemp))); MessageBox(NULL, szTemp, szServiceName, MB_OK | MB_ICONEXCLAMATION); } } } else { // // Okay, we need to copy the instcm.inf file to the cmbins dir and then // call InstallCm // LPCTSTR ArrayOfFileNames[] = { TEXT("cnet16.dll"), TEXT("ccfg95.dll"), TEXT("cmutoa.dll"), TEXT("instcm.inf") // instcm.inf must be last so it is given to InstallCm correctly. }; TCHAR szSource [MAX_PATH+1] = {0}; TCHAR szDest [MAX_PATH+1] = {0}; for (int i = 0; i < (sizeof(ArrayOfFileNames)/sizeof(LPCTSTR)); i++) { MYVERIFY(CELEMS(szDest) > (UINT)wsprintf(szDest, TEXT("%s%s"), szTemp, ArrayOfFileNames[i])); MYVERIFY(CELEMS(szSource) > (UINT)wsprintf(szSource, TEXT("%s%s"), g_szProfileSourceDir, ArrayOfFileNames[i])); MYVERIFY(CopyFile(szSource, szDest, FALSE)); // FALSE == bFailIfExists } hrTemp = InstallCm(hInstance, szDest); } MYDBGASSERT(SUCCEEDED(hrTemp)); bMustReboot = (ERROR_SUCCESS_REBOOT_REQUIRED == hrTemp) ? TRUE: bMustReboot; } else { CMASSERTMSG(FALSE, TEXT("InstallInf -- ExtractCmBinsFromExe failed!")); } } // // Now Create the Connectoid. Even if it fails, continue to install. // if (GetPhoneBookPath(szInstallDir, &pszPhonebook, bInstallForAllUsers)) { BOOL bReturn = WriteCmPhonebookEntry(szServiceName, pszPhonebook, szCmsFile); if (!bReturn && plat.IsAtLeastNT5()) { CMASSERTMSG(FALSE, TEXT("CMSTP Failed to create a pbk entry on NT5, exiting.")); hrReturn = E_FAIL; goto exit; } } else if (plat.IsAtLeastNT5()) { CMASSERTMSG(FALSE, TEXT("CMSTP Failed to get a pbk path on NT5, exiting.")); hrReturn = E_FAIL; goto exit; } // // Now we have all the files installed and the pbk entry written, // finally create the desktop shortcut/GUID // if ((plat.IsWin9x()) || (plat.IsNT4())) { // // If we have a Legacy install, then we need to create a desktop icon // if (!bNoLegacyIcon) { hrTemp = LaunchInfSection(szInfFile, TEXT("Xnstall_Legacy"), szTitle, bSilent); MYDBGASSERT(SUCCEEDED(hrTemp)); bMustReboot = (ERROR_SUCCESS_REBOOT_REQUIRED == hrTemp) ? TRUE: bMustReboot; } } else { // // Create a desktop shortcut if necessary // DeleteNT5ShortcutFromPathAndName(hInstance, szServiceName, bInstallForAllUsers ? CSIDL_COMMON_DESKTOPDIRECTORY : CSIDL_DESKTOPDIRECTORY); if (bCreateDesktopIcon) { HRESULT hr = CreateNT5ProfileShortcut(szServiceName, pszPhonebook, bInstallForAllUsers); MYVERIFY(SUCCEEDED(hr)); } } // // The profile is now basically installed. Before doing any post install commands, lets check to see // if the caller asked us to set this connection as the default connection. If so, then // lets set it here. // if (bSetAsDefault) { MYVERIFY(SetThisConnectionAsDefault(szServiceName)); } // // if we have a preshared key, give it to RAS // if (pszPresharedKey) { if (FALSE == plat.IsAtLeastNT51()) { CMASSERTMSG(0, TEXT("profile has a preshared key on pre-XP platform - we should never get here.")); MYVERIFY(0 != LoadString(hInstance, IDS_PSK_NEEDS_XP, szTemp, CELEMS(szTemp))); MessageBox(NULL, szTemp, szServiceName, MB_OK | MB_ICONERROR); hrReturn = S_FALSE; goto exit; } pfnRasSetCredentialsSpec pfnSetCredentials; hrReturn = E_FAIL; if (FALSE == GetRasApis(NULL, NULL, NULL, NULL, NULL, &pfnSetCredentials)) { CMASSERTMSG(FALSE, TEXT("CMSTP Failed to get RAS API RasSetCredentials, exiting.")); goto exit; } if (lstrlen(pszPresharedKey) > PWLEN) { CMASSERTMSG(FALSE, TEXT("preshared key is larger than RasSetCredentials can handle!")); goto exit; } RASCREDENTIALS * pRasCreds = NULL; pRasCreds = (RASCREDENTIALS *) CmMalloc(sizeof(RASCREDENTIALS)); if (NULL == pRasCreds) { hrReturn = E_OUTOFMEMORY; goto exit; } pRasCreds->dwSize = sizeof(RASCREDENTIALS); pRasCreds->dwMask = RASEO2_UsePreSharedKey; lstrcpyn(pRasCreds->szPassword, pszPresharedKey, lstrlen(pszPresharedKey) + 1); if (0 != pfnSetCredentials(pszPhonebook, szServiceName, pRasCreds, FALSE)) // FALSE => set the credentials { CMASSERTMSG(FALSE, TEXT("CMSTP RasSetCredentials failed, exiting.")); CmFree(pRasCreds); goto exit; } CmFree(pRasCreds); hrReturn = S_OK; // // remove the pre-shared key from the .CMP file // pszCmpFile = szTemp; // re-use szTemp to save stack space and not get into trouble on Win9x MYVERIFY(CELEMS(szTemp) > (UINT)wsprintf(pszCmpFile, TEXT("%s%s.cmp"), g_szProfileSourceDir, szShortServiceName)); if (FileExists(pszCmpFile)) { WritePrivateProfileString(c_pszCmSection, c_pszCmEntryPresharedKey, NULL, pszCmpFile); WritePrivateProfileString(c_pszCmSection, c_pszCmEntryKeyIsEncrypted, NULL, pszCmpFile); } } // // Do any postinstall cmds here // LPTSTR pszPostInstallSection; if (bInstallForAllUsers) { pszPostInstallSection = TEXT("PostInstall"); } else { pszPostInstallSection = TEXT("PostInstall_Single"); } hrTemp = LaunchInfSection(szInfFile, pszPostInstallSection, szTitle, bSilent); MYDBGASSERT(SUCCEEDED(hrTemp)); bMustReboot = (ERROR_SUCCESS_REBOOT_REQUIRED == hrTemp) ? TRUE: bMustReboot; // // Delete the temporary reg key that we used to communicate the install path to the inf // if (ERROR_SUCCESS == RegOpenKeyEx(hBaseKey, pszRegInfCommKeyPath, 0, KEY_ALL_ACCESS, &hKey)) { MYVERIFY(ERROR_SUCCESS == RegDeleteValue(hKey, c_pszProfileInstallPath)); MYVERIFY(ERROR_SUCCESS == RegCloseKey(hKey)); } else { CMASSERTMSG(FALSE, TEXT("Unable to delete the ProfileInstallPath temporary Reg value.")); } // // Refresh the desktop so that any GUID or shortcut changes will appear // RefreshDesktop(); // // For Win98 and Millennium, we write an App Compatibility flag in order to // fix SetForegroundWindow. Refer also to Q135788 for more details of the // original fix (which requires this extra code on Win9x to actually work). // // This fixes Whistler bugs 41696 and 90576. // if (plat.IsWin98()) { if (!WriteProfileString(TEXT("Compatibility95"), TEXT("CMMON32"), TEXT("0x00000002"))) { CMTRACE(TEXT("InstallInf - failed to write app compat entry for CMMON32 to fix SetForegroundWindow")); } } // // We are finally completed. If we need to reboot, show the user the reboot prompt. // Otherwise, show the user a completion message. // if (bMustReboot) { MYVERIFY(0 != LoadString(hInstance, IDS_REBOOT_MSG, szTemp, CELEMS(szTemp))); int iRes = MessageBoxEx(NULL, szTemp, szServiceName, MB_YESNO | MB_DEFBUTTON1 | MB_ICONWARNING | MB_SETFOREGROUND, LANG_USER_DEFAULT); if (IDYES == iRes) { // // Shutdown Windows // DWORD dwReason = plat.IsAtLeastNT51() ? (SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_INSTALLATION) : 0; MyExitWindowsEx(EWX_REBOOT, dwReason); } } else if (!bSilent) { // // Instead of giving the user a message box, we will launch the profile // for them. (NTRAID 201307) // if (plat.IsAtLeastNT5()) { pCmstpMutex->Unlock(); //NTRAID 310478 } MYVERIFY(CELEMS(szTemp) > (UINT)wsprintf(szTemp, TEXT("%s\\%s.cmp"), szInstallDir, szShortServiceName)); LaunchProfile(szTemp, szServiceName, pszPhonebook, bInstallForAllUsers); } exit: CmFree(pszPresharedKey); CmFree(pszPhonebook); return hrReturn; }