Files
meteor/scripts/windows/installer/WiXHelper/CustomAction.cpp
2015-02-26 12:09:30 -08:00

448 lines
13 KiB
C++
Executable File

#include "stdafx.h"
#include <string>
#include <wcautil.h>
#include <strutil.h>
#include <pathutil.h>
#include <fileutil.h>
#include <dirutil.h>
#include <urlmon.h>
#include <winhttp.h>
#include <sys/stat.h>
#define BUF_LEN 1024
#define MAX_LONG_PATH 2048
#define LOG true
using namespace std;
class MyCallback : public IBindStatusCallback
{
public:
MSIHANDLE iHInstall;
MyCallback() {}
~MyCallback() { }
// This one is called by URLDownloadToFile
STDMETHOD(OnProgress)(/* [in] */ ULONG ulProgress, /* [in] */ ULONG ulProgressMax, /* [in] */ ULONG ulStatusCode, /* [in] */ LPCWSTR wszStatusText)
{
PMSIHANDLE hActionRec = MsiCreateRecord(3);
PMSIHANDLE hProgressRec = MsiCreateRecord(3);
DWORD ulPrc = 0;
WCHAR wzInfo[1024] = { };
if (ulProgressMax > 0)
{
ulPrc = static_cast<DWORD>(100 * static_cast<double>(ulProgress) / static_cast<double>(ulProgressMax));
::StringCchPrintfW(wzInfo, countof(wzInfo), L"Downloading Meteor package ... %u%%", ulPrc);
}
else
::StringCchPrintfW(wzInfo, countof(wzInfo), L"Downloading Meteor package ...");
MsiRecordSetString(hActionRec, 1, TEXT("Download_MeteorPackage"));
MsiRecordSetString(hActionRec, 2, wzInfo);
MsiRecordSetString(hActionRec, 3, NULL);
UINT iResult = MsiProcessMessage(iHInstall, INSTALLMESSAGE_ACTIONSTART, hActionRec);
if ((iResult == IDCANCEL) || (iResult == IDABORT))
return E_ABORT;
return S_OK;
}
// The rest don't do anything...
STDMETHOD(OnStartBinding)(/* [in] */ DWORD dwReserved, /* [in] */ IBinding __RPC_FAR *pib)
{ return E_NOTIMPL; }
STDMETHOD(GetPriority)(/* [out] */ LONG __RPC_FAR *pnPriority)
{ return E_NOTIMPL; }
STDMETHOD(OnLowResource)(/* [in] */ DWORD reserved)
{ return E_NOTIMPL; }
STDMETHOD(OnStopBinding)(/* [in] */ HRESULT hresult, /* [unique][in] */ LPCWSTR szError)
{ return E_NOTIMPL; }
STDMETHOD(GetBindInfo)(/* [out] */ DWORD __RPC_FAR *grfBINDF, /* [unique][out][in] */ BINDINFO __RPC_FAR *pbindinfo)
{ return E_NOTIMPL; }
STDMETHOD(OnDataAvailable)(/* [in] */ DWORD grfBSCF, /* [in] */ DWORD dwSize, /* [in] */ FORMATETC __RPC_FAR *pformatetc, /* [in] */ STGMEDIUM __RPC_FAR *pstgmed)
{ return E_NOTIMPL; }
STDMETHOD(OnObjectAvailable)(/* [in] */ REFIID riid, /* [iid_is][in] */ IUnknown __RPC_FAR *punk)
{ return E_NOTIMPL; }
// IUnknown stuff
STDMETHOD_(ULONG,AddRef)()
{ return 0; }
STDMETHOD_(ULONG,Release)()
{ return 0; }
STDMETHOD(QueryInterface)(/* [in] */ REFIID riid, /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
{ return E_NOTIMPL; }
};
HRESULT ExtractBinary(
__in LPCWSTR wzBinaryId,
__out BYTE** pbData,
__out DWORD* pcbData
)
{
HRESULT hr = S_OK;
LPWSTR pwzSql = NULL;
PMSIHANDLE hView;
PMSIHANDLE hRec;
// make sure we're not horked from the get-go
hr = WcaTableExists(L"Binary");
if (S_OK != hr)
{
if (SUCCEEDED(hr))
{
hr = E_UNEXPECTED;
}
ExitOnFailure(hr, "There is no Binary table.");
}
ExitOnNull(wzBinaryId, hr, E_INVALIDARG, "Binary ID cannot be null");
ExitOnNull(*wzBinaryId, hr, E_INVALIDARG, "Binary ID cannot be empty string");
hr = StrAllocFormatted(&pwzSql, L"SELECT `Data` FROM `Binary` WHERE `Name`=\'%s\'", wzBinaryId);
ExitOnFailure(hr, "Failed to allocate Binary table query.");
hr = WcaOpenExecuteView(pwzSql, &hView);
ExitOnFailure(hr, "Failed to open view on Binary table");
hr = WcaFetchSingleRecord(hView, &hRec);
ExitOnFailure(hr, "Failed to retrieve request from Binary table");
hr = WcaGetRecordStream(hRec, 1, pbData, pcbData);
ExitOnFailure(hr, "Failed to read Binary.Data.");
LExit:
ReleaseStr(pwzSql);
return hr;
}
HRESULT ExtractBinaryToFile(
__in LPCWSTR wzBinaryId,
__in LPCWSTR wzFilePath
)
{
HRESULT hr = S_OK;
BYTE* pbData = NULL;
DWORD cbData = 0;
DWORD cbWritten = 0;
HANDLE hFile = INVALID_HANDLE_VALUE;
wchar_t szTmpFile[BUF_LEN] = L""; DWORD nTmpFileLen = BUF_LEN;
hr = ExtractBinary(wzBinaryId, &pbData, &cbData);
hFile = CreateFile(wzFilePath, GENERIC_WRITE,FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE) {
WriteFile(hFile, pbData, cbData, &cbWritten, NULL);
CloseHandle(hFile);
}
else
{
hr = HRESULT_FROM_WIN32(::GetLastError());
}
return hr;
}
BOOL ExecuteCommandLine(LPWSTR CommandLine, DWORD & exitCode)
{
PROCESS_INFORMATION processInformation = {0};
STARTUPINFO startupInfo = {0};
startupInfo.cb = sizeof(startupInfo);
// Create the process
BOOL result = CreateProcess(NULL, CommandLine,
NULL, NULL, FALSE,
NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW,
NULL, NULL, &startupInfo, &processInformation);
if (!result)
{
// CreateProcess() failed; Get the error from the system
LPVOID lpMsgBuf;
DWORD dw = GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL);
// Display the error
LPTSTR strError = (LPTSTR) lpMsgBuf;
// Free resources created by the system
LocalFree(lpMsgBuf);
// We failed.
return FALSE;
}
else
{
// Successfully created the process. Wait for it to finish.
WaitForSingleObject( processInformation.hProcess, INFINITE );
// Get the exit code.
result = GetExitCodeProcess(processInformation.hProcess, &exitCode);
// Close the handles.
CloseHandle( processInformation.hProcess );
CloseHandle( processInformation.hThread );
if (!result)
return FALSE;
// We succeeded.
return TRUE;
}
}
HRESULT UnzipToFolder(
MSIHANDLE hInstall,
__in LPCWSTR wzFriendlyName,
__in LPCWSTR wzTarGzFileName,
__in LPCWSTR wzDestPath
)
{
HRESULT hr = S_OK;
WcaLog(LOGMSG_STANDARD, "Extract \"%S\" package initialized.", wzFriendlyName);
wchar_t szSourceDir[BUF_LEN] = L""; DWORD nSourceDirDirLen = BUF_LEN;
wchar_t szTarGzFilePath[BUF_LEN] = L"";
wchar_t szTarFilePath[BUF_LEN] = L"";
MsiGetProperty(hInstall, L"SourceDir", szSourceDir, &nSourceDirDirLen);
StringCchPrintf(szTarGzFilePath, BUF_LEN, L"%s%s", szSourceDir, wzTarGzFileName);
StringCchPrintf(szTarFilePath, BUF_LEN, L"%s*.tar", szSourceDir);
DWORD pdwAttr;
if (FileExistsEx(szTarGzFilePath, &pdwAttr) == TRUE)
{
//Extacting quality_cloud_production.sql to %TEMP% folder
wchar_t szTmpDir[BUF_LEN] = L""; DWORD nTmpDirLen = BUF_LEN;
wchar_t sz7Zip[BUF_LEN] = L"";
wchar_t szCommandLine1[BUF_LEN] = L"";
wchar_t szCommandLine2[BUF_LEN] = L"";
MsiGetProperty(hInstall, L"TempFolder", szTmpDir, &nTmpDirLen);
StringCchPrintf(sz7Zip, BUF_LEN, L"%s%s", szTmpDir, L"7za.exe");
DWORD pdwAttr;
if (FileExistsEx(sz7Zip, &pdwAttr) == FALSE)
{
hr = ExtractBinaryToFile(L"SevenZip", sz7Zip);
}
StringCchPrintf(szCommandLine1, BUF_LEN, L"\"%s\" x -o\"%s\" -y \"%s\"", sz7Zip, szSourceDir, szTarGzFilePath);
StringCchPrintf(szCommandLine2, BUF_LEN, L"\"%s\" x -o\"%s\" -y \"%s\"", sz7Zip, wzDestPath, szTarFilePath);
DWORD nRes=0;
LPTSTR ErrorMessage = NULL;
ExecuteCommandLine(szCommandLine1, nRes);
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, HRESULT_FROM_WIN32(nRes), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&ErrorMessage, 0, NULL);
if (NULL != ErrorMessage) WcaLog(LOGMSG_STANDARD, "Archive expanding completed with (%d): %S", nRes, ErrorMessage);
ExecuteCommandLine(szCommandLine2, nRes);
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, HRESULT_FROM_WIN32(nRes), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&ErrorMessage, 0, NULL);
if (NULL != ErrorMessage) WcaLog(LOGMSG_STANDARD, "Archive deployment completed with (%d): %S", nRes, ErrorMessage);
}
else
{
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
WcaLog(LOGMSG_STANDARD, "Failed to extract %S files. File not found: %S", wzFriendlyName, szTarGzFilePath);
}
WcaLog(LOGMSG_STANDARD, "Extracting \"%S\" package completed.", wzFriendlyName);
return hr;
}
UINT __stdcall Extract_MeteorFiles(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
hr = WcaInitialize(hInstall, "Extract_MeteorFiles");
ExitOnFailure(hr, "Failed to initialize Extract_MeteorFiles");
wchar_t szMeteorDir[BUF_LEN] = L""; DWORD nMeteorDirLen = BUF_LEN;
MsiGetProperty(hInstall, L"METEOR_DIR", szMeteorDir, &nMeteorDirLen);
hr = UnzipToFolder(hInstall, L"Meteor", L"meteor-bootstrap-os.windows.x86_32.tar.gz", szMeteorDir);
ExitOnFailure(hr, "Failed to extract Meteor files.");
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
HRESULT Download_Package(
MSIHANDLE hInstall,
__in LPCWSTR wzFriendlyName,
__in LPCWSTR wzProperty_DWNURL,
__in LPCWSTR wzZipFile)
{
HRESULT hr = S_OK;
WcaLog(LOGMSG_STANDARD, "Download package \"%S\" initialized.", wzFriendlyName);
wchar_t szSourceDir[BUF_LEN] = L""; DWORD nSourceDirDirLen = BUF_LEN;
wchar_t szDwnUrl[BUF_LEN] = L""; DWORD nDwnUrlLen = BUF_LEN;
wchar_t szDwnUser[BUF_LEN] = L""; DWORD nDwnUserLen = BUF_LEN;
wchar_t szDwnPass[BUF_LEN] = L""; DWORD nDwnPassLen = BUF_LEN;
wchar_t szZipFile[BUF_LEN] = L"";
MsiGetProperty(hInstall, L"SourceDir", szSourceDir, &nSourceDirDirLen);
MsiGetProperty(hInstall, wzProperty_DWNURL, szDwnUrl, &nDwnUrlLen);
MsiGetProperty(hInstall, L"HTTP_DWN_USER", szDwnUser, &nDwnUserLen);
MsiGetProperty(hInstall, L"HTTP_DWN_PASS", szDwnPass, &nDwnPassLen);
StringCchPrintf(szZipFile, BUF_LEN, L"%s%s", szSourceDir, wzZipFile);
// Checking for Prerequisites\localFile
wchar_t szBundleSrc[BUF_LEN] = L""; DWORD nBundleSrcLen = BUF_LEN;
wchar_t szPrereqDir[BUF_LEN] = L""; DWORD nPrereqDirLen = BUF_LEN;
wchar_t szLocalFile[BUF_LEN] = L"";
wchar_t* szBundlePath;
MsiGetProperty(hInstall, L"BUNDLE_SOURCE", szBundleSrc, &nBundleSrcLen);
MsiGetProperty(hInstall, L"PREREQ_FOLDER", szPrereqDir, &nPrereqDirLen);
PathGetDirectory(szBundleSrc, &szBundlePath);
StringCchPrintf(szLocalFile, BUF_LEN, L"%s%s\\%s", szBundlePath, szPrereqDir, wzZipFile);
// If local file exists use it instaead of download.
DWORD pdwAttr;
if (FileExistsEx(szLocalFile, &pdwAttr) == TRUE)
{
FileEnsureCopy(szLocalFile, szZipFile, TRUE);
WcaLog(LOGMSG_STANDARD, "Nginx local package found \"%S\", will use that.", szLocalFile);
}
else
{
MyCallback pCallback;
pCallback.iHInstall = hInstall;
hr = URLDownloadToFile(NULL, szDwnUrl, szZipFile, 0, &pCallback);
if (FAILED(hr))
WcaLog(LOGMSG_STANDARD, "Failed to download %S package from url: %S", wzFriendlyName, szDwnUrl);
else
WcaLog(LOGMSG_STANDARD, "%S package should be here: %S", wzFriendlyName, szZipFile);
}
WcaLog(LOGMSG_STANDARD, "Download package \"%S\" completed.", wzFriendlyName);
return hr;
}
UINT __stdcall Download_MeteorPackage(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
hr = WcaInitialize(hInstall, "Download_MeteorPackage");
ExitOnFailure(hr, "Failed to initialize Download_MeteorPackage");
hr = Download_Package(hInstall, L"Meteor", L"METEOR_DWN_URL", L"meteor-bootstrap-os.windows.x86_32.tar.gz");
ExitOnFailure(hr, "Failed to download Meteor package from specified URL.");
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
UINT __stdcall BulkRemoveMeteorFiles(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
hr = WcaInitialize(hInstall, "BulkRemoveMeteorFiles");
ExitOnFailure(hr, "Failed to initialize BulkRemoveMeteorFiles");
WcaLog(LOGMSG_STANDARD, "BulkRemoveMeteorFiles Initialized.");
wchar_t szPathPackages[BUF_LEN] = L""; DWORD nPathPackages = BUF_LEN;
wchar_t szPathPkg_Meta[BUF_LEN] = L""; DWORD nPathPkg_Meta = BUF_LEN;
MsiGetProperty(hInstall, L"METEORDIR_PACKAGES", szPathPackages, &nPathPackages);
MsiGetProperty(hInstall, L"METEORDIR_PKG_META", szPathPkg_Meta, &nPathPkg_Meta);
wchar_t szSysDir[BUF_LEN] = L""; DWORD nSysDirLen = BUF_LEN;
wchar_t szCmd1[BUF_LEN] = L"";
wchar_t szCmd2[BUF_LEN] = L"";
DWORD nRes=0;
MsiGetProperty(hInstall, L"SystemFolder", szSysDir, &nSysDirLen);
StringCchPrintf(szCmd1, BUF_LEN, L"%s\\cmd.exe /C \"RD /S /Q \"%s\">NUL\"", szSysDir, szPathPackages);
StringCchPrintf(szCmd2, BUF_LEN, L"%s\\cmd.exe /C \"RD /S /Q \"%s\">NUL\"", szSysDir, szPathPkg_Meta);
ExecuteCommandLine(szCmd1, nRes);
ExecuteCommandLine(szCmd2, nRes);
WcaLog(LOGMSG_STANDARD, "BulkRemoveMeteorFiles done.");
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
// DllMain - Initialize and cleanup WiX custom action utils.
extern "C" BOOL WINAPI DllMain(
__in HINSTANCE hInst,
__in ULONG ulReason,
__in LPVOID
)
{
switch(ulReason)
{
case DLL_PROCESS_ATTACH:
WcaGlobalInitialize(hInst);
break;
case DLL_PROCESS_DETACH:
WcaGlobalFinalize();
break;
}
return TRUE;
}