Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2009 lines
48 KiB

// SSRTEngine.cpp : Implementation of SSR Engine
#include "stdafx.h"
#include "SSRTE.h"
#include "SSRTEngine.h"
#include "SSRMembership.h"
#include "MemberAccess.h"
#include "SSRLog.h"
#include "ActionData.h"
#include "global.h"
#include "util.h"
/////////////////////////////////////////////////////////////////////////////
// CSsrEngine
/*
Routine Description:
Name:
CSsrEngine::CSsrEngine
Functionality:
constructor
Virtual:
no.
Arguments:
none.
Return Value:
none.
Notes:
*/
CSsrEngine::CSsrEngine()
: m_pActionData(NULL),
m_pMembership(NULL)
{
HRESULT hr = CComObject<CSsrMembership>::CreateInstance(&m_pMembership);
if (FAILED(hr))
{
throw hr;
}
hr = CComObject<CSsrActionData>::CreateInstance(&m_pActionData);
if (FAILED(hr))
{
throw hr;
}
//
// hold on to these objects
//
m_pMembership->AddRef();
m_pActionData->AddRef();
m_pActionData->SetMembership(m_pMembership);
}
/*
Routine Description:
Name:
CSsrEngine::~CSsrEngine
Functionality:
destructor
Virtual:
yes.
Arguments:
none.
Return Value:
none.
Notes:
*/
CSsrEngine::~CSsrEngine()
{
m_pActionData->Release();
m_pMembership->Release();
}
/*
Routine Description:
Name:
CSsrEngine::GetActionData
Functionality:
Retrieve the action data interface
Virtual:
Yes.
Arguments:
ppAD - The out parameter that receives the ISsrActionData interface from
the m_pActionData object
Return Value:
Success:
S_OK.
Failure:
various error codes.
Notes:
*/
HRESULT
CSsrEngine::GetActionData (
OUT ISsrActionData ** ppAD
)
{
HRESULT hr = E_SSR_ACTION_DATA_NOT_AVAILABLE;
if (ppAD == NULL)
{
hr = E_INVALIDARG;
}
else if (m_pActionData != NULL)
{
*ppAD = NULL;
hr = m_pActionData->QueryInterface(IID_ISsrActionData, (LPVOID*)ppAD);
if (S_OK != hr)
{
hr = E_SSR_ACTION_DATA_NOT_AVAILABLE;
}
}
return hr;
}
/*
Routine Description:
Name:
CSsrEngine::DoActionVerb
Functionality:
The engine will perform the action.
Virtual:
Yes.
Arguments:
bstrActionVerb - "configure" or "rollback".
lActionType - The action's type.
varFeedbackSink - The COM interface ISsrFeedbackSink given by the caller.
We will use this interface to call back when we want
to give feedback information to the caller.
lFlag - Reserved for future use.
Return Value:
Success:
S_OK.
Failure:
various error codes.
Notes:
*/
STDMETHODIMP
CSsrEngine::DoActionVerb (
IN BSTR bstrActionVerb,
IN LONG lActionType,
IN VARIANT varFeedbackSink,
IN LONG lFlag
)
{
if (bstrActionVerb == NULL)
{
g_fblog.LogString(IDS_INVALID_PARAMETER, L"bstrActionVerb");
return E_INVALIDARG;
}
SsrActionVerb lActionVerb = SsrPGetActionVerbFromString(bstrActionVerb);
if (lActionVerb == ActionInvalid)
{
g_fblog.LogString(IDS_INVALID_PARAMETER, bstrActionVerb);
return E_SSR_INVALID_ACTION_VERB;
}
//
// we will only accept SSR_ACTION_APPLY or SSR_ACTION_PREPARE
//
if ( (lActionType != SSR_ACTION_PREPARE) &&
(lActionType != SSR_ACTION_APPLY) )
{
g_fblog.LogString(IDS_INVALID_PARAMETER, L"lActionType");
return E_SSR_INVALID_ACTION_TYPE;
}
HRESULT hr;
VARIANT varFeedbackSinkNull;
varFeedbackSinkNull.vt = VT_NULL;
if ( lActionVerb == ActionConfigure && lActionType == SSR_ACTION_APPLY) {
hr = DoActionVerb (
g_bstrRollback,
SSR_ACTION_PREPARE,
varFeedbackSinkNull,
lFlag);
if (FAILED(hr))
{
g_fblog.LogError(hr, L"CSsrEngine", L"Rollback failed");
return hr;
}
}
//
// need to loop through all members
//
hr = m_pMembership->LoadAllMember();
if (hr == E_SSR_MEMBER_XSD_INVALID)
{
//
// logging has been done by LoadAllMembers
//
return hr;
}
CComVariant var;
hr = m_pMembership->GetAllMembers(&var);
if (FAILED(hr))
{
g_fblog.LogError(hr, L"CSsrEngine", L"m_pMembership->GetAllMembers");
return hr;
}
//
// now we have members to work on, so get the feedback sink ready and
// kick out the actions!
//
if (varFeedbackSink.vt == VT_UNKNOWN || varFeedbackSink.vt == VT_DISPATCH)
{
hr = g_fblog.SetFeedbackSink(varFeedbackSink);
if (FAILED(hr))
{
g_fblog.LogError(hr, L"CSsrEngine", L"g_fblog.SetFeedbackSink");
return hr;
}
}
else if (varFeedbackSink.vt != VT_NULL && varFeedbackSink.vt != VT_EMPTY)
{
//
// if the feedback has something other than NULL or empty, but
// it is not an IUnknown or IDispatch, then, we log the error,
// but we will continue
//
g_fblog.LogString(IDS_INVALID_PARAMETER, L"varFeedbackSink");
}
CSafeArray sa(&var);
//
// We have to discover information about total number of items to be processed.
// Currently, this mimics the actual action, which is a little bit costly. We
// should consider some lookup mechanism without going so much to the members.
//
//vector<ULONG> vecTransformIndexes;
//vector<ULONG> vecScriptIndexes;
//vector<ULONG> vecCustomIndexes;
//
// The following actions will be counted:
// (1) Loading security policy XML files
// (2) Transform using one xsl file and the creation of the output
// file count as one.
// (3) executing a script
// (4) any custom actions
//
DWORD dwTotalSteps = 0;
CSsrMemberAccess * pMA;
CComVariant varMemberName;
//
// ask each member to give us the cost for the action
//
for (ULONG i = 0; i < sa.GetSize(); i++)
{
pMA = NULL;
varMemberName.Clear();
//
// see if this member supports the desired action. If it does
// then get the cost.
//
if (SUCCEEDED(sa.GetElement(i, VT_BSTR, &varMemberName)))
{
pMA = m_pMembership->GetMemberByName(varMemberName.bstrVal);
if (pMA != NULL)
{
dwTotalSteps += pMA->GetActionCost(lActionVerb, lActionType);
}
}
}
//
// if we need to do transformation, then let us cleanup the old ones.
// We won't bother to restore them if somehow we fail to do the transformation
// unless we are doing rollback transformation.
// We must not continue if this fails.
//
WCHAR wszTempDir[MAX_PATH + 2];
wszTempDir[MAX_PATH + 1] = L'\0';
if ( (lActionType == SSR_ACTION_PREPARE ) )
{
if ( lActionVerb == ActionRollback )
{
//
// if we need to do "rollback" transformation, then we need to
// backup our previous rollback output files
//
//
// first, we need a temporary directory
//
hr = SsrPCreateUniqueTempDirectory(wszTempDir, MAX_PATH + 1);
if (SUCCEEDED(hr))
{
hr = MoveRollbackFiles(&sa,
SsrPGetDirectory(lActionVerb, TRUE),
wszTempDir,
true // we want logging
);
}
}
else
{
//
// otherwise, we will just delete them and never bother
// to restore them if anything fails
//
hr = CleanupOutputFiles(&sa,
lActionVerb,
true // we want logging
);
}
if (FAILED(hr))
{
g_fblog.LogError(hr, L"CSsrEngine", L"Cleanup previous result files failed.");
return hr;
}
}
//
// Feedback the total steps info
//
g_fblog.SetTotalSteps(dwTotalSteps);
//
// now let's carry out the action. First, feedback/log the action started
// information.
//
g_fblog.LogFeedback(SSR_FB_START,
(DWORD)S_OK,
NULL,
g_dwResNothing
);
//
// we may decide to presson in case of errors, but we should always
// return the error we found
//
HRESULT hrFirstError = S_OK;
CComPtr<IXMLDOMDocument2> srpDomSecurityPolicy;
//
// ask each member to give us the action data so that we can
// carry out the action.
//
for (i = 0; i < sa.GetSize(); i++)
{
pMA = NULL;
varMemberName.Clear();
//
// see if this member supports the desired action. If it does
// then get the cost.
//
if (SUCCEEDED(sa.GetElement(i, VT_BSTR, &varMemberName)))
{
pMA = m_pMembership->GetMemberByName(varMemberName.bstrVal);
if (pMA == NULL)
{
//
// $undone:shawnwu, should we press on? This shouldn't happen.
//
_ASSERT(FALSE);
g_fblog.LogString(IDS_MISSING_MEMBER, varMemberName.bstrVal);
continue;
}
CMemberAD* pmemberAD = pMA->GetActionDataObject(lActionVerb,
lActionType
);
if (pmemberAD == NULL)
{
//
// $undone:shawnwu, should we press on? This shouldn't happen.
//
_ASSERT(FALSE);
g_fblog.LogString(IDS_MEMBER_NOT_SUPPORT_ACTION, varMemberName.bstrVal);
continue;
}
int iCount = pmemberAD->GetProcedureCount();
//
// mark the entry point for carrying out the action
//
g_fblog.LogFeedback(SSR_FB_START_MEMBER_ACTION,
varMemberName.bstrVal,
bstrActionVerb,
g_dwResNothing
);
for (int iProc = 0; iProc < iCount; iProc++)
{
const CSsrProcedure * pSsrProc = pmemberAD->GetProcedure(iProc);
if (pSsrProc->IsDefaultProcedure())
{
int iFilePairCount = pSsrProc->GetFilePairCount();
for (int iPair = 0; iPair < iFilePairCount; iPair++)
{
//
// get the file pair information so that we can
// determine which action to carry out
//
CSsrFilePair * pfp = pSsrProc->GetFilePair(iPair);
if (pfp->GetFirst() != NULL)
{
//
// if the first file is there, then, we will do
// a transformation.
//
hr = DoTransforms(lActionVerb,
pfp,
&srpDomSecurityPolicy,
lFlag
);
}
else if (pfp->GetSecond() != NULL &&
pfp->IsExecutable() )
{
hr = RunScript(SsrPGetDirectory(lActionVerb, TRUE),
pfp->GetSecond()
);
}
if (hrFirstError == S_OK && FAILED(hr))
{
hrFirstError = hr;
}
g_fblog.Steps(2);
if (!SsrPPressOn(lActionVerb, lActionType, hr))
{
break;
}
}
}
else
{
if (pSsrProc->GetProgID() == NULL &&
pMA->GetProgID() == NULL)
{
//
// we can't do anything because the progID is missing
//
g_fblog.LogString(IDS_MISSING_PROGID, varMemberName.bstrVal);
}
else
{
hr = DoCustom(lActionVerb,
lActionType,
(pSsrProc->GetProgID() != NULL) ? pSsrProc->GetProgID() : pMA->GetProgID(),
varFeedbackSink,
lFlag
);
//
// Feedback steps are counted by the custom member itself
// via the feedback sink we give.
//
}
if (hrFirstError == S_OK && FAILED(hr))
{
hrFirstError = hr;
}
if (!SsrPPressOn(lActionVerb, lActionType, hr))
{
break;
}
}
if (hrFirstError == S_OK && FAILED(hr))
{
hrFirstError = hr;
}
if (!SsrPPressOn(lActionVerb, lActionType, hr))
{
break;
}
}
g_fblog.LogFeedback(SSR_FB_END_MEMBER_ACTION,
varMemberName.bstrVal,
bstrActionVerb,
g_dwResNothing
);
}
}
if (SUCCEEDED(hrFirstError) &&
(lActionType == SSR_ACTION_PREPARE) &&
(lActionVerb == ActionRollback) )
{
//
// transform succeeded, then we need to get rid of the files
// backed up for rollback.
//
::SsrPDeleteEntireDirectory(wszTempDir);
}
else if ( (lActionType == SSR_ACTION_PREPARE) && (lActionVerb == ActionRollback))
{
//
// transform failed, we need to restore the backup files
// for the rollback. First, we must remove all output files
//
HRESULT hrRestore = CleanupOutputFiles(&sa,
lActionVerb,
false // we don't want logging
);
//
// we will purposely leave the rollback files backed up
// in previous steps in case we failed to restore all of them
// so that we can at least log it and let the user do
// the restoration
//
if (SUCCEEDED(hrRestore))
{
hrRestore = MoveRollbackFiles(&sa,
wszTempDir,
g_wszSsrRoot,
false // no logging
);
if (SUCCEEDED(hrRestore))
{
::SsrPDeleteEntireDirectory(wszTempDir);
}
else
{
g_fblog.LogError(hr,
L"CSsrEngine",
L"Restore old rollback files failed. These files are located at the directory whose name is the following guid:"
);
g_fblog.LogString(wszTempDir);
}
}
}
//
// now action has complete! Give the HRESULT as feedback.
// Also, we always give back S_OK as the return result if
// everything goes on fine.
//
if (SUCCEEDED(hrFirstError))
{
hrFirstError = S_OK;
}
g_fblog.LogFeedback(SSR_FB_END | FBLog_Log,
hr,
NULL,
g_dwResNothing
);
g_fblog.TerminateFeedback();
return hrFirstError;
}
/*
Routine Description:
Name:
CSsrEngine::DoCustom
Functionality:
We will delegate to the objects of custom implementation for this action.
Virtual:
no.
Arguments:
lActionVerb - action verb
bstrProgID - The member's ProgID
varFeedbackSink - the sink interface if any.
lFlag - reserved for future use.
Return Value:
Success:
various success codes returned from DOM or ourselves.
Use SUCCEEDED(hr) to test.
Failure:
various error codes returned from DOM or ourselves.
Use FAILED(hr) to test.
Notes:
*/
HRESULT
CSsrEngine::DoCustom (
IN SsrActionVerb lActionVerb,
IN LONG lActionType,
IN const BSTR bstrProgID,
IN VARIANT varFeedbackSink,
IN LONG lFlag
)
{
g_fblog.LogString(IDS_START_CUSTOM, NULL);
GUID clsID;
CComPtr<ISsrMember> srpMember;
CComVariant varAD;
HRESULT hr = ::CLSIDFromProgID(bstrProgID, &clsID);
if (S_OK == hr)
{
hr = ::CoCreateInstance(clsID,
NULL,
CLSCTX_INPROC_SERVER,
IID_ISsrMember,
(LPVOID*)&srpMember
);
}
if (SUCCEEDED(hr))
{
varAD.vt = VT_UNKNOWN;
varAD.punkVal = NULL;
//
// this m_pActionData must have ISsrActionData unless exception
//
hr = m_pActionData->QueryInterface(IID_ISsrActionData,
(LPVOID*)&(varAD.punkVal)
);
if (FAILED(hr))
{
g_fblog.LogError(hr,
bstrProgID,
L"m_pActionData->QueryInterface failed"
);
}
}
else
{
g_fblog.LogString(IDS_MISSING_CUSTOM_MEMBER, bstrProgID);
}
//
// if we can't provide an log object, then the custom
// object should create one by itself
//
if (SUCCEEDED(hr))
{
CComVariant varLog;
g_fblog.GetLogObject(&varLog);
//
// custom object must let us pass in action context
//
hr = srpMember->SetActionContext(varAD, varLog, varFeedbackSink);
if (FAILED(hr))
{
g_fblog.LogError(hr,
bstrProgID,
L"SetActionContext failed."
);
}
}
//
// logging and feedback will be done by the custom objects
//
if (SUCCEEDED(hr))
{
hr = srpMember->DoActionVerb(SsrPGetActionVerbString(lActionVerb), lActionType, lFlag);
if (FAILED(hr))
{
g_fblog.LogError(hr,
L"DoActionVerb",
SsrPGetActionVerbString(lActionVerb)
);
}
}
g_fblog.LogString(IDS_END_CUSTOM, NULL);
return hr;
}
/*
Routine Description:
Name:
CSsrEngine::DoTransforms
Functionality:
We perform our well defined XSLT transformation action.
Virtual:
no.
Arguments:
lActionVerb - action verb
pfp - CSsrFilePair object that contains the xsl and output file
name information. Output file information may be empty,
in which case in means that the the transformation does
require to create an output file.
ppXmlDom - The security policy DOM object. If this is a NULL object,
then this function will create and load it for later use.
lFlag - The flag that determines the transformation characteristics.
The one we heavily used is SSR_LOADDOM_VALIDATE_ON_PARSE.
This can be bitwise OR'ed. If this is set to 0, then we
use the registered flags for each individual member.
In other words, this flag overwrites the registered flag
if it is not 0.
Return Value:
Success:
various success codes returned from DOM or ourselves.
Use SUCCEEDED(hr) to test.
Failure:
various error codes returned from DOM or ourselves.
Use FAILED(hr) to test.
Notes:
*/
HRESULT
CSsrEngine::DoTransforms (
IN SsrActionVerb lActionVerb,
IN CSsrFilePair * pfp,
IN OUT IXMLDOMDocument2 ** ppXmlDom,
IN LONG lFlag
)
{
g_fblog.LogString(IDS_START_XSL_TRANSFORM, NULL);
//
// We will prepare an XML dom object if it doesn't pass in one.
//
HRESULT hr = S_OK;
if (*ppXmlDom == NULL)
{
//
// First of all, we need the SecurityPolicy.xml file
//
CComVariant varXmlPolicy;
hr = m_pActionData->GetProperty(CComBSTR(g_pwszCurrSecurityPolicy),
&varXmlPolicy
);
//
// Loading the security policy XML file is considered SSR Engine actions
//
if (S_OK != hr)
{
g_fblog.LogFeedback(SSR_FB_ERROR_CRITICAL | FBLog_Log,
(LPCWSTR)NULL,
NULL,
IDS_MISSING_SECPOLICY
);
return hr;
}
else if (varXmlPolicy.vt != VT_BSTR)
{
g_fblog.LogFeedback(SSR_FB_ERROR_CRITICAL | FBLog_Log,
(LPCWSTR)NULL,
g_pwszCurrSecurityPolicy,
IDS_SECPOLICY_INVALID_TYPE
);
return hr;
}
CComBSTR bstrSecPolicy(varXmlPolicy.bstrVal);
// bstrSecPolicy += L"\\Policies\\";
// bstrSecPolicy = varXmlPolicy.bstrVal;
if (bstrSecPolicy.m_str == NULL)
{
return E_OUTOFMEMORY;
}
hr = ::CoCreateInstance(CLSID_DOMDocument40,
NULL,
CLSCTX_SERVER,
IID_IXMLDOMDocument2,
(LPVOID*)(ppXmlDom)
);
if (SUCCEEDED(hr))
{
hr = SsrPLoadDOM(bstrSecPolicy, lFlag, (*ppXmlDom));
if (SUCCEEDED(hr))
{
//
// we loaded the security policy xml file
//
g_fblog.LogFeedback(FBLog_Log,
hr,
bstrSecPolicy,
IDS_LOAD_SECPOLICY
);
}
else
{
g_fblog.LogFeedback(SSR_FB_ERROR_CRITICAL | FBLog_Log,
hr,
bstrSecPolicy,
IDS_LOAD_SECPOLICY
);
return hr;
}
}
else
{
g_fblog.LogError(
hr,
L"Can't create CLSID_DOMDocument40 object",
NULL
);
return hr;
}
}
//
// let's check if all section of the security policy xml file
// contains any section that we have no member that understands
//
CComBSTR bstrUnknownMember, bstrExtraInfo;
HRESULT hrCheck;
hrCheck = VerifyDOM((*ppXmlDom), &bstrUnknownMember, &bstrExtraInfo);
if (bstrUnknownMember.Length() > 0)
{
g_fblog.LogFeedback(SSR_FB_ERROR_UNKNOWN_MEMBER | FBLog_Log,
bstrUnknownMember,
bstrExtraInfo,
g_dwResNothing
);
}
//
// let's create the XSL template object
//
CComPtr<IXSLTemplate> srpIXSLTemplate;
hr = ::CoCreateInstance(CLSID_XSLTemplate,
NULL,
CLSCTX_SERVER,
IID_IXSLTemplate,
(LPVOID*)(&srpIXSLTemplate)
);
if (FAILED(hr))
{
g_fblog.LogFeedback(SSR_FB_ERROR_CRITICAL | FBLog_Log,
hr,
NULL,
IDS_FAIL_CREATE_XSLT
);
return hr;
}
//
// now ready to do member wise transform
//
BSTR bstrXslDir = SsrPGetDirectory(lActionVerb, FALSE);
BSTR bstrResultDir = SsrPGetDirectory(lActionVerb, TRUE);
hr = DoMemberTransform(
pfp,
bstrXslDir,
bstrResultDir,
(*ppXmlDom),
srpIXSLTemplate,
lFlag
);
if (FAILED(hr))
{
g_fblog.LogError(hr,
L"DoTransforms",
pfp->GetFirst()
);
}
g_fblog.LogString(IDS_END_XSL_TRANSFORM, NULL);
return hr;
}
/*
Routine Description:
Name:
CSsrEngine::DoMemberTransform
Functionality:
We will do the private transform and then create the output file.
Virtual:
no.
Arguments:
pfp - CSsrFilePair object that contains names of
both xsl and output files. If the latter is empty,
it means it doesn't need to create an output file.
pwszXslFilesDir - the XSL file directory
pwszResultFilesDir - the output file directory
pXmlDOM - The XML DOM object interface
pXslTemplate - The XSL template object interface
lFlag - The flag that determines the transformation characteristics.
The one we heavily used is SSR_LOADDOM_VALIDATE_ON_PARSE.
Return Value:
Success:
various success codes returned from DOM or ourselves.
Use SUCCEEDED(hr) to test.
Failure:
various error codes returned from DOM or ourselves.
Use FAILED(hr) to test.
Notes:
*/
HRESULT
CSsrEngine::DoMemberTransform (
IN CSsrFilePair * pfp,
IN LPCWSTR pwszXslFilesDir,
IN LPCWSTR pwszResultFilesDir,
IN IXMLDOMDocument2 * pXmlDOM,
IN IXSLTemplate * pXslTemplate,
IN LONG lFlag
)
{
HRESULT hr = S_OK;
CComBSTR bstrXslFilePath(pwszXslFilesDir);
bstrXslFilePath += L"\\";
bstrXslFilePath += pfp->GetFirst();
CComBSTR bstrResultFilePath;
if (pfp->GetSecond() != NULL)
{
bstrResultFilePath = pwszResultFilesDir;
bstrResultFilePath += L"\\";
bstrResultFilePath += pfp->GetSecond();
}
hr = Transform (bstrXslFilePath,
bstrResultFilePath,
pXmlDOM,
pXslTemplate,
lFlag
);
DWORD dwID = SUCCEEDED(hr) ? IDS_TRANSFORM_SUCCEEDED : IDS_TRANSFORM_FAILED;
g_fblog.LogFeedback(SSR_FB_TRANSFORM_RESULT | FBLog_Log,
hr,
pfp->GetFirst(),
dwID
);
return hr;
}
/*
Routine Description:
Name:
CSsrEngine::Transform
Functionality:
We will do the private transform and then create the output file.
Virtual:
no.
Arguments:
bstrXslPath - The xsl file path.
bstrResultPath - the output file path
pXmlDOM - The XML DOM object interface
pXslTemplate - The XSL template object interface
lFlag - The flag that determines the transformation characteristics.
The one we heavily used is SSR_LOADDOM_VALIDATE_ON_PARSE.
Return Value:
Success:
various success codes returned from DOM or ourselves.
Use SUCCEEDED(hr) to test.
Failure:
various error codes returned from DOM or ourselves.
Use FAILED(hr) to test.
Notes:
*/
HRESULT
CSsrEngine::Transform (
IN BSTR bstrXslPath,
IN BSTR bstrResultPath,
IN IXMLDOMDocument2 * pXmlDOM,
IN IXSLTemplate * pXslTemplate,
IN LONG lFlag
)
{
CComBSTR bstrResult;
HRESULT hr = S_OK;
if (bstrResultPath != NULL)
{
//
// we need to create a result file
// using our transformation result.
//
hr = PrivateTransform (
bstrXslPath,
pXmlDOM,
pXslTemplate,
lFlag,
&bstrResult
);
}
else
{
hr = PrivateTransform (
bstrXslPath,
pXmlDOM,
pXslTemplate,
lFlag,
NULL
);
}
//
// we allow transform to have no text results. Or there is no
// output file given. In either case, the effect is simply to call
// the transformation, which may still do meaningful things.
//
if (SUCCEEDED(hr) &&
bstrResult.m_str != NULL &&
bstrResultPath != NULL &&
*bstrResultPath != L'\0' )
{
//
// if there is an output file that needs to be created.
//
HANDLE hFile = ::CreateFile(bstrResultPath,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (hFile != INVALID_HANDLE_VALUE)
{
//
// write the results into the file
//
long lLen = wcslen(bstrResult);
DWORD dwWritten = 0;
BOOL bStatus = ::WriteFile(hFile,
bstrResult.m_str,
lLen * sizeof(WCHAR),
&dwWritten,
NULL
);
::CloseHandle(hFile);
if (!bStatus)
{
g_fblog.LogFeedback(SSR_FB_ERROR_FILE_WRITE | FBLog_Log,
GetLastError(),
bstrResultPath,
IDS_FILEWRITE_FAILED
);
hr = E_FAIL;
}
}
else
{
g_fblog.LogFeedback(SSR_FB_ERROR_FILE_CREATE | FBLog_Log,
GetLastError(),
bstrResultPath,
IDS_FILECREATE_FAILED
);
hr = E_FAIL;
}
}
return hr;
}
/*
Routine Description:
Name:
CSsrEngine::PrivateTransform
Functionality:
Do the real XSLT transformation
Virtual:
no.
Arguments:
bstrXsl - the XSL file path
pxmlDom - The XML DOM object interface
pxslTemplate- The XSL template object interface
uFlag - The flag that determines the transformation
characteristics. The one we heavily used is
SSR_LOADDOM_VALIDATE_ON_PARSE.
pbstrResult - The result string.
Return Value:
Success:
various success codes returned from DOM or ourselves.
Use SUCCEEDED(hr) to test.
Failure:
various error codes returned from DOM or ourselves.
Use FAILED(hr) to test.
Notes:
*/
HRESULT
CSsrEngine::PrivateTransform (
IN BSTR bstrXsl,
IN IXMLDOMDocument2 * pxmlDom,
IN IXSLTemplate * pxslTemplate,
IN LONG lFlag,
OUT BSTR * pbstrResult OPTIONAL
)
{
if (bstrXsl == NULL ||
*bstrXsl == L'\0' ||
pxmlDom == NULL ||
pxslTemplate == NULL)
{
return E_INVALIDARG;
}
CComPtr<IXMLDOMDocument2> srpXsl;
HRESULT hr = ::CoCreateInstance(CLSID_FreeThreadedDOMDocument,
NULL,
CLSCTX_SERVER, IID_IXMLDOMDocument2,
(LPVOID*)(&srpXsl)
);
if (SUCCEEDED(hr))
{
hr = SsrPLoadDOM(bstrXsl, lFlag, srpXsl);
}
if (FAILED(hr))
{
return hr;
}
short sResult = FALSE;
CComPtr<IXSLProcessor> srpIXSLProcessor;
if (pbstrResult != NULL)
{
*pbstrResult = NULL;
}
hr = pxslTemplate->putref_stylesheet(srpXsl);
if (FAILED(hr))
{
g_fblog.LogString(IDS_XSL_TRANSFORM_FAILED, L"putref_stylesheet");
}
if (SUCCEEDED(hr))
{
hr = pxslTemplate->createProcessor(&srpIXSLProcessor);
if (FAILED(hr))
{
g_fblog.LogString(IDS_CREATE_IXSLPROC_FAILED, NULL);
}
}
if (SUCCEEDED(hr))
{
CComVariant varIDoc2(pxmlDom);
hr = srpIXSLProcessor->put_input(varIDoc2);
if (SUCCEEDED(hr))
{
hr = srpIXSLProcessor->transform(&sResult);
if (SUCCEEDED(hr) && (sResult == VARIANT_TRUE))
{
//
// if we want results back
//
if (pbstrResult != NULL)
{
VARIANT varValue;
::VariantInit(&varValue);
hr = srpIXSLProcessor->get_output(&varValue);
//
// if the output is successffuly retrieved,
// then it is owned by the out parameter
//
if (SUCCEEDED(hr) && varValue.vt == VT_BSTR)
{
*pbstrResult = varValue.bstrVal;
//
// the bstr value is owned by the output parameter now
//
varValue.vt = VT_EMPTY;
varValue.bstrVal = NULL;
}
else
{
::VariantClear(&varValue);
}
}
}
else
{
g_fblog.LogString(IDS_XSL_TRANSFORM_FAILED,
L"IXSLProcessor->transform"
);
}
}
else
{
g_fblog.LogString(IDS_XSL_TRANSFORM_FAILED,
L"IXSLProcessor->put_input"
);
}
}
return hr;
}
/*
Routine Description:
Name:
CSsrEngine::RunScript
Functionality:
We will launch all given scripts
Virtual:
No.
Arguments:
bstrDirPath - The path of the directory where the scripts resides
bstrScriptFile - The script file's name
Return Value:
Success:
S_OK of some scripts are run.
S_FALSE if no scripts can be found to run.
Failure:
various error codes.
Notes:
1. We should try to hide the cmd window.
*/
HRESULT
CSsrEngine::RunScript (
IN BSTR bstrDirPath,
IN BSTR bstrScriptFile
)
{
if (bstrDirPath == NULL || bstrScriptFile == NULL)
{
return E_INVALIDARG;
}
//
// create the script file's full path
//
int iLen = wcslen(bstrDirPath) + 1 + wcslen(bstrScriptFile) + 1;
LPWSTR pwszFilePath = new WCHAR[iLen];
if (pwszFilePath == NULL)
{
return E_OUTOFMEMORY;
}
//
// don't return blindly w/o freeing the pwszFilePath from this point on
//
HRESULT hr = S_OK;
_snwprintf(pwszFilePath, iLen, L"%s\\%s", bstrDirPath, bstrScriptFile);
WIN32_FILE_ATTRIBUTE_DATA wfad;
if ( !GetFileAttributesEx(pwszFilePath, GetFileExInfoStandard, &wfad) )
{
g_fblog.LogFeedback(SSR_FB_ERROR_FILE_MISS | FBLog_Log,
hr,
pwszFilePath,
IDS_CANNOT_ACCESS_FILE
);
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
}
if (SUCCEEDED(hr) && !IsScriptFile(pwszFilePath))
{
g_fblog.LogString(IDS_NOT_SUPPORTED_SCRIPT_FILE_TYPE,
pwszFilePath
);
hr = S_FALSE;
}
if (S_OK == hr)
{
//
// now kick out the script
//
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
WCHAR wszExitCode[g_dwHexDwordLen];
g_fblog.LogString(IDS_RUNNING_SCRIPTS, pwszFilePath);
CComBSTR bstrCmdLine(L"CScript.exe //B ");
bstrCmdLine += pwszFilePath;
if (bstrCmdLine.m_str == NULL)
{
hr = E_OUTOFMEMORY;
}
else
{
//
// this will launch the script w/o a window
//
BOOL bRun = ::CreateProcess(
NULL,
bstrCmdLine,
NULL,
NULL,
FALSE,
CREATE_NO_WINDOW,
NULL,
NULL,
&si,
&pi
);
if (!bRun)
{
g_fblog.LogFeedback(SSR_FB_ERROR_CRITICAL | FBLog_Log,
GetLastError(),
bstrCmdLine,
IDS_ERROR_CREATE_PROCESS
);
//
// $undone:shawnwu, should we quit?
//
}
else
{
//
// we don't proceed until the script ends
// and then we log the exit code
//
::WaitForSingleObject( pi.hProcess, INFINITE );
DWORD dwExitCode;
bRun = ::GetExitCodeProcess(pi.hProcess, &dwExitCode);
_snwprintf(wszExitCode, g_dwHexDwordLen, L"0x%X", dwExitCode);
g_fblog.LogFeedback(SSR_FB_EXIT_CODE | FBLog_Log,
(LPCWSTR)NULL,
wszExitCode,
IDS_EXIT_CODE
);
}
}
}
delete [] pwszFilePath;
return hr;
}
/*
Routine Description:
Name:
CSsrEngine::IsScriptFile
Functionality:
test if a file is a script file
Virtual:
No.
Arguments:
pwszFileName - The path of the file
Return Value:
true if and only if it is one of the script file types (.vbs, .js, .wsf)
Notes:
*/
bool
CSsrEngine::IsScriptFile (
IN LPCWSTR pwszFileName
)const
{
//
// check if the the file is indeed a script
//
if (pwszFileName == NULL)
{
return false;
}
LPCWSTR pwszExt = pwszFileName + wcslen(pwszFileName) - 1;
while (pwszExt != pwszFileName)
{
if (*pwszExt == L'.')
{
break;
}
else
{
pwszExt--;
}
}
return (_wcsicmp(L".js", pwszExt) == 0 ||
_wcsicmp(L".vbs", pwszExt) == 0 ||
_wcsicmp(L".wsf", pwszExt) == 0 );
}
/*
Routine Description:
Name:
CSsrEngine::VerifyDOM
Functionality:
Will check if the every section of the security policy has a member
to process it.
Virtual:
No.
Arguments:
pXmlPolicy - The XML policy DOM.
pbstrUnknownMember - receives the unknown member's name if found.
pbstrExtraInfo - receives extra info, such as whether the missing
member is from local system or from the security policy
Return Value:
Success: S_OK
Failure: various error codes
Notes:
$undone:shawnwu
Harmless to call, but no actio is taken at this time. Waiting for
finalization of security policy schema.
*/
HRESULT
CSsrEngine::VerifyDOM (
IN IXMLDOMDocument2 * pXmlPolicy,
OUT BSTR * pbstrUnknownMember,
OUT BSTR * pbstrExtraInfo
)
{
HRESULT hr = S_OK;
if (pbstrUnknownMember == NULL)
{
hr = E_INVALIDARG;
}
else
{
*pbstrUnknownMember = NULL;
}
if (pbstrExtraInfo == NULL)
{
hr = E_INVALIDARG;
}
else
{
*pbstrExtraInfo = NULL;
}
if (pXmlPolicy == NULL)
{
hr = E_INVALIDARG;
}
return hr;
}
/*
Routine Description:
Name:
CSsrEngine::CleanupOutputFiles
Functionality:
Will clean up all those transformation output files for the given
action.
Virtual:
No.
Arguments:
psaMemberNames - The names of the members
lAction - The action verb.
bLog - If false, there will be no logging. This prevents
extra logging during restoration of failed rollback
transformation.
Return Value:
Success: S_OK
Failure: various error codes
Notes:
We must not blindly delete all the files in the output directory
because some of them may be installed by a member. Only those transformation
files we are told to generate will be cleanup.
*/
HRESULT
CSsrEngine::CleanupOutputFiles (
IN CSafeArray * psaMemberNames,
IN SsrActionVerb lAction,
IN bool bLog
)
{
HRESULT hr = S_OK;
HRESULT hrLastError = S_OK;
//
// log the action
//
if (bLog)
{
g_fblog.LogString(IDS_START_CLEANUP_CONFIGURE_OUTPUTS, NULL);
}
//
// we will try to finish the work even if errors occur. However
// we will return such error.
//
for (ULONG i = 0; i < psaMemberNames->GetSize(); i++)
{
CComVariant varName;
//
// Get the indexed element as a bstr = the name of the member
//
hr = psaMemberNames->GetElement(i, VT_BSTR, &varName);
if (SUCCEEDED(hr))
{
CSsrMemberAccess * pMA = m_pMembership->GetMemberByName(
varName.bstrVal);
if (pMA != NULL)
{
//
// want output file directory (true inside SsrPGetDirectory)
//
hr = pMA->MoveOutputFiles(lAction,
SsrPGetDirectory(lAction, TRUE),
NULL,
true,
bLog
);
if (FAILED(hr))
{
hrLastError = hr;
}
}
}
}
if (bLog)
{
g_fblog.LogString(IDS_END_CLEANUP_CONFIGURE_OUTPUTS, NULL);
}
return hrLastError;
}
/*
Routine Description:
Name:
CSsrEngine::MoveRollbackFiles
Functionality:
Will move the rollback files (only those transformation output files
for rollback) in the source directory root to the destination
directory root.
Virtual:
No.
Arguments:
psaMemberNames - The names of the all members
pwszSrcDirPath - The path of the source directory from which the files
will to be moved.
pwszDestDirRoot - The path of the destination directory to which the files
will be moved .
Return Value:
Success: S_OK
Failure: various error codes
Notes:
*/
HRESULT
CSsrEngine::MoveRollbackFiles (
IN CSafeArray * psaMemberNames,
IN LPCWSTR pwszSrcDirPath,
IN LPCWSTR pwszDestDirPath,
IN bool bLog
)
{
HRESULT hr = S_OK;
HRESULT hrLastError = S_OK;
//
// it's the output files of rollback transformation that
// need to be moved.
//
if (bLog)
{
g_fblog.LogString(IDS_START_BACKUP_ROLLBACK_OUTPUTS, NULL);
}
//
// for each member, we need to move the rollback files
//
for (ULONG i = 0; i < psaMemberNames->GetSize(); i++)
{
CComVariant varName;
//
// this is the i-th member's name
//
hr = psaMemberNames->GetElement(i, VT_BSTR, &varName);
if (SUCCEEDED(hr))
{
//
// get this member's information access class
//
CSsrMemberAccess * pMA = m_pMembership->GetMemberByName(varName.bstrVal);
_ASSERT(pMA != NULL);
hr = pMA->MoveOutputFiles(ActionRollback,
pwszSrcDirPath,
pwszDestDirPath,
false, // don't delete
bLog
);
}
}
if (bLog)
{
g_fblog.LogString(IDS_END_BACKUP_ROLLBACK_OUTPUTS, NULL);
}
return hrLastError;
}