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

//+----------------------------------------------------------------------------
//
// 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 <advpub.h>
#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;
}