windows-nt/Source/XPSP1/NT/enduser/windows.com/iuengine/manifest.cpp
2020-09-26 16:20:57 +08:00

1074 lines
29 KiB
C++

//=======================================================================
//
// Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved.
//
// File: manifest.cpp
//
// Description:
//
// Implementation for the GetManifest() function
//
//=======================================================================
#include "iuengine.h"
#include <iucommon.h>
#include <fileutil.h>
#include <shlwapi.h>
#include <wininet.h>
#include "schemamisc.h"
#include "WaitUtil.h"
#include "download.h"
#include <httprequest.h>
#include <httprequest_i.c>
#include <iuxml.h>
#define QuitIfNull(p) {if (NULL == p) {hr = E_INVALIDARG; LOG_ErrorMsg(hr); return hr;}}
#define QuitIfFail(x) {hr = x; if (FAILED(hr)) goto CleanUp;}
#define ERROR_INVALID_PID 100
#define E_INVALID_PID MAKE_HRESULT(SEVERITY_ERROR,FACILITY_ITF,ERROR_INVALID_PID)
#define errorInvalidLicense 1
const TCHAR g_szInvalidPID[] = _T("The PID is invalid");
const TCHAR IDENT_IUSCHEMA[] = _T("IUSchema");
const TCHAR IDENT_IUSCHEMA_SOAPQUERY[] = _T("SOAPQuerySchema");
const TCHAR IDENT_IUSERVERCACHE[] = _T("IUServerCache");
const TCHAR IDENT_IUSERVERCOUNT[] = _T("ServerCount");
const TCHAR IDENT_IUSERVER[] = _T("Server");
const CHAR SZ_GET_MANIFEST[] = "Querying software update catalog from";
const CHAR SZ_GET_MANIFEST_ERROR[] = "Querying software update catalog";
HRESULT ValidatePID(IXMLDOMDocument *pXmlDomDocument);
void PingInvalidPID(BSTR bstrClientName,HRESULT hRes,HANDLE *phQuit,DWORD dwNumHandles);
/////////////////////////////////////////////////////////////////////////////
// Function forward declarations
/////////////////////////////////////////////////////////////////////////////
HRESULT GetServerURL(IXMLDOMDocument *pXMLQuery, IXMLDOMDocument *pXMLClientInfo, LPTSTR *ppszURL);
HRESULT GetSOAPQuery(IXMLDOMDocument *pXMLClientInfo, IXMLDOMDocument *pXMLSystemSpec,
IXMLDOMDocument *pXMLQuery, IXMLDOMDocument **ppSOAPQuery);
/////////////////////////////////////////////////////////////////////////////
// GetManifest()
//
// Gets a catalog base on the specified information.
// Input:
// bstrXmlClientInfo - the credentials of the client in xml format
// bstrXmlSystemSpec - the detected system specifications in xml
// bstrXmlQuery - the user query infomation in xml
// Return:
// pbstrXmlCatalog - the xml catalog retrieved
/////////////////////////////////////////////////////////////////////////////
HRESULT WINAPI CEngUpdate::GetManifest(BSTR bstrXmlClientInfo,
BSTR bstrXmlSystemSpec,
BSTR bstrXmlQuery,
DWORD dwFlags,
BSTR *pbstrXmlCatalog)
{
LOG_Block("GetManifest()");
// clear any previous cancel event
ResetEvent(m_evtNeedToQuit);
USES_IU_CONVERSION;
HRESULT hr = E_FAIL;
CXmlClientInfo xmlClientInfo;
IXMLDOMDocument *pXMLSystemSpec = NULL;
IXMLDOMDocument *pXMLQuery = NULL;
IXMLDOMDocument *pSOAPQuery = NULL;
IXMLHttpRequest *pIXMLHttpRequest = NULL;
IWinHttpRequest *pWinHttpRequest = NULL;
BSTR bstrXmlSOAPQuery = NULL;
BSTR bstrPOST = NULL, bstrURL = NULL;
BSTR bstrClientName = NULL;
LPTSTR pszURL = NULL;
LONG lCount = 0;
MSG msg;
DWORD dwRet;
BOOL fDontAllowProxy = FALSE;
SAUProxySettings pauProxySettings;
ZeroMemory(&pauProxySettings, sizeof(SAUProxySettings));
//
// load the DOM Doc for Query, ClientInfo, SystemSpec respectively
//
LOG_XmlBSTR(bstrXmlQuery);
hr = LoadXMLDoc(bstrXmlQuery, &pXMLQuery, FALSE);
CleanUpIfFailedAndMsg(hr);
LOG_XmlBSTR(bstrXmlClientInfo);
hr = xmlClientInfo.LoadXMLDocument(bstrXmlClientInfo, FALSE);
CleanUpIfFailedAndMsg(hr);
CleanUpIfFailedAndSetHrMsg(xmlClientInfo.GetClientName(&bstrClientName));
CleanUpIfFailedAndSetHrMsg(g_pUrlAgent->IsClientSpecifiedByPolicy(OLE2T(bstrClientName)));
//
// Set flag to NOT set proxy for WinHTTP
//
if (S_FALSE ==hr)
{
fDontAllowProxy = FALSE;
hr = S_OK;
}
else // S_OK
{
fDontAllowProxy = TRUE;
}
//
// we treat bstrXmlSystemSpec as optional
//
if (NULL != bstrXmlSystemSpec && SysStringLen(bstrXmlSystemSpec) > 0)
{
LOG_XmlBSTR(bstrXmlSystemSpec);
hr = LoadXMLDoc(bstrXmlSystemSpec, &pXMLSystemSpec, FALSE);
CleanUpIfFailedAndMsg(hr);
}
//
// retrieve the ServerCache URL from the Query xml doc and validate it
//
hr = GetServerURL(pXMLQuery, xmlClientInfo.GetDocument(), &pszURL);
CleanUpIfFailedAndMsg(hr);
//
// concatenate the above several xml client input into a single XML
// with the SOAP syntax/format that the server recognizes
//
hr = GetSOAPQuery(xmlClientInfo.GetDocument(), pXMLSystemSpec, pXMLQuery, &pSOAPQuery);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
goto CleanUp;
}
#if defined(DBG)
else
{
BSTR bstrSOAPQuery = NULL;
pSOAPQuery->get_xml(&bstrSOAPQuery);
LOG_XmlBSTR(bstrSOAPQuery);
SafeSysFreeString(bstrSOAPQuery);
}
#endif
//
// change again: add WINHTTP support for AU running as a service;
// use GetAllowedDownloadTransport(0) to determine -
// 1) 0 == try winhttp first & if that fails, try wininet.
// 2) WUDF_ALLOWWINHTTPONLY == only try winhttp. never fall back on wininet.
// 3) WUDF_ALLOWWININETONLY == only try wininet. never use winhttp.
//
// in WINHTTP the compression is not supported yet at this time;
// in WININET we removed the compression support at this point due to a bug in URLMON (< IE6.0).
//
// in both cases we use asynchronized sending in order to abort timely for a cancel event
//
BOOL fLoadWINHTTP = FALSE;
DWORD dwTransportFlag = GetAllowedDownloadTransport(0);
if ((0 == dwTransportFlag) || (WUDF_ALLOWWINHTTPONLY == dwTransportFlag))
{
hr = CoCreateInstance(CLSID_WinHttpRequest,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWinHttpRequest,
(void **) &pWinHttpRequest);
if (SUCCEEDED(hr))
{
BOOL fRetry = FALSE;
bstrURL = SysAllocString(T2OLE(pszURL));
fLoadWINHTTP = TRUE;
VARIANT vProxyServer, vBypassList;
hr = GetAUProxySettings(bstrURL, &pauProxySettings);
CleanUpIfFailedAndMsg(hr);
DWORD iProxy = pauProxySettings.iProxy;
if (-1 == iProxy)
pauProxySettings.iProxy = iProxy = 0;
//
// open request
//
VARIANT vBool;
vBool.vt = VT_BOOL;
vBool.boolVal = VARIANT_TRUE;
bstrPOST = SysAllocString(L"POST");
Retry:
hr = pWinHttpRequest->Open(bstrPOST, // HTTP method: "POST"
bstrURL, // requested URL
vBool); // asynchronous operation
CleanUpIfFailedAndMsg(hr);
//
// For SSL URLs, set the WinHttpRequestOption_SslErrorIgnoreFlags
// option to zero so that no server certificate errors are ignored.
//
// The default at the time this code was written was 0x3300, which
// means ignore all server certificate errors. Using this default
// would significantly reduce security in various ways; for example
// if the 0x0100 bit was set, WinHttp would trust certificates from
// any root certificate authority, even it it was not in the list of
// trusted CAs.
//
// Note that at the time this code was written, the WinHttp
// documentation made no mention of Certificate Revocation List
// checking. It is assumed that the default CRL behavior
// implemented by WinHttp will provide adequate security and
// performance.
//
if ((_T('H') == pszURL[0] || _T('h') == pszURL[0]) && // Sorry, this is simpler than using a function.
(_T('T') == pszURL[1] || _T('t') == pszURL[1]) &&
(_T('T') == pszURL[2] || _T('t') == pszURL[2]) &&
(_T('P') == pszURL[3] || _T('p') == pszURL[3]) &&
(_T('S') == pszURL[4] || _T('s') == pszURL[4]) &&
_T(':') == pszURL[5])
{
VARIANT vOption;
VariantInit(&vOption);
vOption.vt = VT_I4;
vOption.lVal = 0;
hr = pWinHttpRequest->put_Option(WinHttpRequestOption_SslErrorIgnoreFlags, vOption);
VariantClear(&vOption);
CleanUpIfFailedAndMsg(hr);
}
if (TRUE == fDontAllowProxy)
{
LOG_Internet(_T("Don't set the proxy due to policy"));
}
else
{
//
// set proxy
//
VariantInit(&vProxyServer);
VariantInit(&vBypassList);
BOOL fSetProxy = TRUE;
if (pauProxySettings.rgwszProxies != NULL)
{
vProxyServer.vt = VT_BSTR;
vProxyServer.bstrVal = SysAllocString(pauProxySettings.rgwszProxies[iProxy]);
}
else if (pauProxySettings.rgwszProxies == NULL)
{
fSetProxy = FALSE;
}
if (pauProxySettings.wszBypass != NULL)
{
vBypassList.vt = VT_BSTR;
vBypassList.bstrVal = SysAllocString(pauProxySettings.wszBypass);
}
if (fSetProxy)
{
hr = pWinHttpRequest->SetProxy(HTTPREQUEST_PROXYSETTING_PROXY, vProxyServer, vBypassList);
}
VariantClear(&vProxyServer);
VariantClear(&vBypassList);
CleanUpIfFailedAndMsg(hr);
}
//
// send request
//
VARIANT vQuery;
vQuery.vt = VT_UNKNOWN;
vQuery.punkVal = pSOAPQuery;
hr = pWinHttpRequest->Send(vQuery);
if (FAILED(hr))
{
LOG_Internet(_T("WinHttpRequest: Send failed: 0x%08x"), hr);
fRetry = TRUE;
goto getNextProxyForRetry;
}
//
// check if quit or completion every 1/4 second
//
VARIANT vTimeOut;
vTimeOut.vt = VT_I4;
vTimeOut.lVal = 0;
VARIANT_BOOL fSuccess = VARIANT_FALSE;
hr = pWinHttpRequest->WaitForResponse(vTimeOut, &fSuccess);
if (FAILED(hr))
{
LOG_Internet(_T("WinHttpRequest: WaitForResponse failed: 0x%08x"), hr);
fRetry = TRUE;
goto getNextProxyForRetry;
}
// we wait up to 30 sec (120*250 ms)
lCount = 0;
while (!fSuccess && lCount <120)
{
lCount++;
//
// Wait for 250ms while pumping messages, but return if m_evtNeedToQuit signaled
//
dwRet = MyMsgWaitForMultipleObjects(1, &m_evtNeedToQuit, FALSE, 250, QS_ALLINPUT);
if (WAIT_TIMEOUT != dwRet)
{
//
// Either the event was signaled or a message being pumped says quit
//
pWinHttpRequest->Abort();
hr = E_ABORT;
goto CleanUp;
}
hr = pWinHttpRequest->WaitForResponse(vTimeOut, &fSuccess);
if (FAILED(hr))
{
LOG_Internet(_T("WinHttpRequest: WaitForResponse failed: 0x%08x"), hr);
fRetry = TRUE;
goto getNextProxyForRetry;
}
}
//
// check the HTTP status code returned by a request
//
LONG lStatus = HTTP_STATUS_OK;// 200
hr = pWinHttpRequest->get_Status(&lStatus);
if (FAILED(hr))
{
LOG_Internet(_T("WinHttpRequest: get_Status failed: 0x%08x"), hr);
fRetry = TRUE;
goto getNextProxyForRetry;
}
fRetry = FALSE;
if (!fSuccess)
{
// time out
hr = E_FAIL;
fRetry = TRUE;
}
else if (HTTP_STATUS_OK != lStatus)
{
// COMPLETED, but error in status
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_HTTP, lStatus);
LOG_ErrorMsg(hr);
fRetry = TRUE;
}
else
{
//
// get response
//
hr = pWinHttpRequest->get_ResponseText(pbstrXmlCatalog);
CleanUpIfFailedAndMsg(hr);
//
// verify the response is a well-formed XML document
//
IXMLDOMDocument *pXMLDoc = NULL;
hr = LoadXMLDoc(*pbstrXmlCatalog, &pXMLDoc);
if(SUCCEEDED(hr))
{
hr=ValidatePID(pXMLDoc);
if(FAILED(hr))
{
PingInvalidPID(bstrClientName,hr,&m_evtNeedToQuit,1);
LogError(hr,"Validation of PID failed");
}
//The Banned PID case is not a failure for the GetManifest call.
if(E_INVALID_PID == hr)
{
hr = S_OK;
}
}
SafeReleaseNULL(pXMLDoc);
CleanUpIfFailedAndMsg(hr);
}
getNextProxyForRetry:
if (fRetry && !fDontAllowProxy)
{
if (pauProxySettings.cProxies > 1 && pauProxySettings.rgwszProxies != NULL)
{
iProxy = ( iProxy + 1) % pauProxySettings.cProxies;
}
if (iProxy != pauProxySettings.iProxy)
{
LogError(hr, "Will retry.");
pWinHttpRequest->Abort();
goto Retry;
}
else
{
LogError(hr, "Already tried all proxies. Will not retry.");
}
}
}
else
{
if (WUDF_ALLOWWINHTTPONLY == dwTransportFlag)
{
CleanUpIfFailedAndMsg(hr);
}
}
}
if ((0 == dwTransportFlag && !fLoadWINHTTP) || (WUDF_ALLOWWININETONLY == dwTransportFlag))
{
//
// 475506 W2K: IU - IU control's GetManifest method call fails on all subsequent
// calls after the first. - On Win 2K Only
//
// We no longer take the FLAG_USE_COMPRESSION into account for WinInet since we
// previously used URLMON and there are bugs that would require a rewrite of
// xmlhttp.* to fix and we haven't been using compression on the live site to date.
//
LOG_Internet(_T("GetManifest using WININET.DLL"));
//
// create an XMLHttpRequest object
//
hr = CoCreateInstance(CLSID_XMLHTTPRequest,
NULL,
CLSCTX_INPROC_SERVER,
IID_IXMLHttpRequest,
(void **) &pIXMLHttpRequest);
CleanUpIfFailedAndMsg(hr);
//
// open request
//
VARIANT vEmpty, vBool;
vEmpty.vt = VT_EMPTY;
vBool.vt = VT_BOOL;
vBool.boolVal= VARIANT_FALSE;
bstrPOST = SysAllocString(L"POST");
bstrURL = SysAllocString(T2OLE(pszURL));
hr = pIXMLHttpRequest->open(bstrPOST, // HTTP method: "POST"
bstrURL, // requested URL
vBool, // synchronous operation
vEmpty, // user for authentication (no authentication for V1.0)
vEmpty); // pswd for authentication
CleanUpIfFailedAndMsg(hr);
//
// send request
//
VARIANT vQuery;
vQuery.vt = VT_UNKNOWN;
vQuery.punkVal = pSOAPQuery;
hr = pIXMLHttpRequest->send(vQuery);
CleanUpIfFailedAndMsg(hr);
//
// check the HTTP status code returned by a request
//
LONG lResultStatus = HTTP_STATUS_OK;// 200
hr = pIXMLHttpRequest->get_status(&lResultStatus);
CleanUpIfFailedAndMsg(hr);
if (HTTP_STATUS_OK != lResultStatus)
{
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_HTTP, lResultStatus);
LOG_ErrorMsg(hr);
}
else
{
//
// get response
//
hr = pIXMLHttpRequest->get_responseText(pbstrXmlCatalog);
CleanUpIfFailedAndMsg(hr);
//
// verify the response is a well-formed XML document
//
IXMLDOMDocument *pXMLDoc = NULL;
hr = LoadXMLDoc(*pbstrXmlCatalog, &pXMLDoc);
if(SUCCEEDED(hr))
{
hr=ValidatePID(pXMLDoc);
if(FAILED(hr))
{
PingInvalidPID(bstrClientName,hr,&m_evtNeedToQuit,1);
LogError(hr,"Validation of PID failed");
}
//The Banned pid case is not a failure for the GetManifest call
if(E_INVALID_PID == hr)
{
hr = S_OK;
}
}
SafeReleaseNULL(pXMLDoc);
CleanUpIfFailedAndMsg(hr);
}
}
CleanUp:
if (SUCCEEDED(hr))
{
#if defined(UNICODE) || defined(_UNICODE)
LogMessage("%s %ls", SZ_GET_MANIFEST, pszURL);
#else
LogMessage("%s %s", SZ_GET_MANIFEST, pszURL);
#endif
}
else
{
if (NULL == pszURL)
{
LogError(hr, SZ_GET_MANIFEST_ERROR);
}
else
{
#if defined(UNICODE) || defined(_UNICODE)
LogError(hr, "%s %ls", SZ_GET_MANIFEST, pszURL);
#else
LogError(hr, "%s %s", SZ_GET_MANIFEST, pszURL);
#endif
}
}
if (NULL != pszURL)
HeapFree(GetProcessHeap(), 0, pszURL);
SafeReleaseNULL(pXMLSystemSpec);
SafeReleaseNULL(pXMLQuery);
SafeReleaseNULL(pSOAPQuery);
SafeReleaseNULL(pIXMLHttpRequest);
SafeReleaseNULL(pWinHttpRequest);
SysFreeString(bstrPOST);
SysFreeString(bstrURL);
SysFreeString(bstrXmlSOAPQuery);
SysFreeString(bstrClientName);
FreeAUProxySettings(&pauProxySettings);
return hr;
}
/////////////////////////////////////////////////////////////////////////////
// GetServerURL()
//
// Retrieve the ServerCache URL from the Query xml doc and validate that
// URL against the ServerCache URLs in iuident.txt.
// Return:
// ppszURL - the ServerCache URL path pointer
/////////////////////////////////////////////////////////////////////////////
HRESULT GetServerURL(IXMLDOMDocument *pXMLQuery, IXMLDOMDocument *pXMLClientInfo, LPTSTR *ppszURL)
{
LOG_Block("GetServerURL()");
USES_IU_CONVERSION;
HRESULT hr = E_FAIL;
if ((NULL == pXMLQuery) || (NULL == pXMLClientInfo) || (NULL == ppszURL))
{
LOG_ErrorMsg(E_INVALIDARG);
return E_INVALIDARG;
}
IXMLDOMNode* pQueryNode = NULL;
IXMLDOMNode* pQueryClient = NULL;
BSTR bstrQuery = SysAllocString(L"query");
BSTR bstrHref = SysAllocString(L"href");
BSTR bstrClientInfo = SysAllocString(L"clientInfo");
BSTR bstrClientName = SysAllocString(L"clientName");
BSTR bstrURL = NULL, bstrClient = NULL;
LPTSTR pszURL = NULL;
INT iServerCnt;
BOOL fInternalServer = FALSE;
QuitIfFail(FindSingleDOMNode(pXMLClientInfo, bstrClientInfo, &pQueryClient));
QuitIfFail(GetAttribute(pQueryClient, bstrClientName, &bstrClient));
pszURL = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, INTERNET_MAX_URL_LENGTH * sizeof(TCHAR));
CleanUpFailedAllocSetHrMsg(pszURL);
//
// failure in "g_pUrlAgent = new CUrlAgent"
//
CleanUpFailedAllocSetHrMsg(g_pUrlAgent);
QuitIfFail(g_pUrlAgent->GetQueryServer(OLE2T(bstrClient), pszURL, INTERNET_MAX_URL_LENGTH, &fInternalServer));
if (fInternalServer)
{
//
// we have policy override for this client, set the query url as WUServer in policy
//
*ppszURL = pszURL;
hr = S_OK;
}
else
{
//
// we don't have policy override for this client;
//
// find the ServerCache URL from <query> node
//
QuitIfFail(FindSingleDOMNode(pXMLQuery, bstrQuery, &pQueryNode));
if (SUCCEEDED(GetAttribute(pQueryNode, bstrHref, &bstrURL))
&& NULL != bstrURL && SysStringLen(bstrURL) >0)
{
//
// this is the case that the query specified the serverl url, we need
// to do the validation for the url here...
//
// pszURL is alloced to be INTERNET_MAX_URL_LENGTH above.
hr = StringCchCopyEx(pszURL, INTERNET_MAX_URL_LENGTH, OLE2T(bstrURL),
NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
goto CleanUp;
}
//
// process the iuident.txt to find all valid ServerCache URLs
//
TCHAR szIUDir[MAX_PATH];
TCHAR szIdentFile[MAX_PATH];
GetIndustryUpdateDirectory(szIUDir);
hr = PathCchCombine(szIdentFile, ARRAYSIZE(szIdentFile), szIUDir, IDENTTXT);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
goto CleanUp;
}
iServerCnt = GetPrivateProfileInt(IDENT_IUSERVERCACHE,
IDENT_IUSERVERCOUNT,
-1,
szIdentFile);
if (-1 == iServerCnt)
{
// no ServerCount number specified in iuident.txt
LOG_Error(_T("No ServerCount number specified in iuident.txt"));
hr = E_FAIL;
goto CleanUp;
}
hr = INET_E_INVALID_URL;
for (INT i=1; i<=iServerCnt; i++)
{
TCHAR szValidURL[INTERNET_MAX_URL_LENGTH];
TCHAR szServer[32];
hr = StringCchPrintfEx(szServer, ARRAYSIZE(szServer), NULL, NULL, MISTSAFE_STRING_FLAGS,
_T("%s%d"), IDENT_IUSERVER, i);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
goto CleanUp;
}
hr = INET_E_INVALID_URL;
GetPrivateProfileString(IDENT_IUSERVERCACHE,
szServer,
_T(""),
szValidURL,
ARRAYSIZE(szValidURL),
szIdentFile);
if ('\0' == szValidURL[0])
{
// no ServerCache URL specified in iuident.txt for this server
LOG_Error(_T("No ServerCache URL specified in iuident.txt for %s%d"), IDENT_IUSERVER, i);
hr = E_FAIL;
goto CleanUp;
}
if (0 == lstrcmpi(szValidURL, pszURL))
{
// it's a valid ServerCache URL
*ppszURL = pszURL;
hr = S_OK;
break;
}
}
}
else
{
//
// this is the case that the query didn't specify the serverl url, we just use the
// server url that was found through g_pUrlAgent according to the clientName.
//
// now insert the URL into the <query> node
//
BSTR bstrTemp = T2BSTR(pszURL);
QuitIfFail(SetAttribute(pQueryNode, bstrHref, bstrTemp));
SafeSysFreeString(bstrTemp);
*ppszURL = pszURL;
}
}
CleanUp:
if (FAILED(hr))
{
HeapFree(GetProcessHeap(), 0, pszURL);
*ppszURL = NULL;
LOG_ErrorMsg(hr);
}
SafeReleaseNULL(pQueryNode);
SafeReleaseNULL(pQueryClient);
SysFreeString(bstrQuery);
SysFreeString(bstrHref);
SysFreeString(bstrURL);
SysFreeString(bstrClientInfo);
SysFreeString(bstrClientName);
SysFreeString(bstrClient);
return hr;
}
/////////////////////////////////////////////////////////////////////////////
// GetSOAPQuery()
//
// Concatenate the several xml client input into a single XML
// with the SOAP syntax/format that the server recognizes
// Input:
// pXMLClientInfo - the credentials of the client in DOM Doc format
// pXMLSystemSpec - the detected system specifications in DOM Doc
// pXMLQuery - the user query infomation in DOM Doc
// Return:
// ppSOAPQuery - the concatenated query in DOM Doc with required SOAP syntax
//
// SOAPQuery xml doc example:
// <SOAP:Envelope xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/">
// <SOAP:Body>
// <GetManifest>
// <clientInfo>...</clientInfo>
// <systemSpec>...</systemSpec>
// <query href="//windowsupdate.microsoft.com/servecache.asp">...</query>
// </GetManifest>
// </SOAP:Body>
// </SOAP:Envelope>
//
/////////////////////////////////////////////////////////////////////////////
HRESULT GetSOAPQuery(IXMLDOMDocument *pXMLClientInfo,
IXMLDOMDocument *pXMLSystemSpec,
IXMLDOMDocument *pXMLQuery,
IXMLDOMDocument **ppSOAPQuery)
{
LOG_Block("GetSOAPQuery()");
USES_IU_CONVERSION;
HRESULT hr = E_FAIL;
IXMLDOMDocument* pDocSOAPQuery = NULL;
IXMLDOMNode* pNodeSOAPEnvelope = NULL;
IXMLDOMNode* pNodeSOAPBody = NULL;
IXMLDOMNode* pNodeGetManifest = NULL;
IXMLDOMNode* pNodeClientInfo = NULL;
IXMLDOMNode* pNodeClientInfoNew = NULL;
IXMLDOMNode* pNodeSystemInfo = NULL;
IXMLDOMNode* pNodeSystemInfoNew = NULL;
IXMLDOMNode* pNodeQuery = NULL;
IXMLDOMNode* pNodeQueryNew = NULL;
BSTR bstrNameSOAPEnvelope = SysAllocString(L"SOAP:Envelope");
BSTR bstrNameSOAPBody = SysAllocString(L"SOAP:Body");
BSTR bstrNameGetManifest = SysAllocString(L"GetManifest");
BSTR bstrClientInfo = SysAllocString(L"clientInfo");
BSTR bstrSystemInfo = SysAllocString(L"systemInfo");
BSTR bstrQuery = SysAllocString(L"query");
BSTR bstrNameSpaceSchema = NULL;
//
// process the iuident.txt to find the SOAPQuery schema path
//
TCHAR szIUDir[MAX_PATH];
TCHAR szIdentFile[MAX_PATH];
LPTSTR pszSOAPQuerySchema = NULL;
LPTSTR pszNameSpaceSchema = NULL;
pszSOAPQuerySchema = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, INTERNET_MAX_URL_LENGTH * sizeof(TCHAR));
if (NULL == pszSOAPQuerySchema)
{
hr = E_OUTOFMEMORY;
LOG_ErrorMsg(hr);
goto CleanUp;
}
pszNameSpaceSchema = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, INTERNET_MAX_URL_LENGTH * sizeof(TCHAR));
if (NULL == pszNameSpaceSchema)
{
hr = E_OUTOFMEMORY;
LOG_ErrorMsg(hr);
goto CleanUp;
}
GetIndustryUpdateDirectory(szIUDir);
hr = PathCchCombine(szIdentFile, ARRAYSIZE(szIdentFile), szIUDir, IDENTTXT);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
goto CleanUp;
}
GetPrivateProfileString(IDENT_IUSCHEMA,
IDENT_IUSCHEMA_SOAPQUERY,
_T(""),
pszSOAPQuerySchema,
INTERNET_MAX_URL_LENGTH,
szIdentFile);
if ('\0' == pszSOAPQuerySchema[0])
{
// no SOAPQuery schema path specified in iuident.txt
LOG_Error(_T("No schema path specified in iuident.txt for SOAPQuery"));
hr = E_FAIL;
goto CleanUp;
}
// pszNameSpaceSchema is alloced to be INTERNET_MAX_URL_LENGTH above
hr = StringCchPrintfEx(pszNameSpaceSchema, INTERNET_MAX_URL_LENGTH, NULL, NULL, MISTSAFE_STRING_FLAGS,
_T("x-schema:%s"), pszSOAPQuerySchema);
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
goto CleanUp;
}
bstrNameSpaceSchema = T2BSTR(pszNameSpaceSchema);
//
// construct the SOAPQuery xml
//
QuitIfFail(CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument, (void **)&pDocSOAPQuery));
pNodeSOAPEnvelope = CreateDOMNode(pDocSOAPQuery, NODE_ELEMENT, bstrNameSOAPEnvelope, bstrNameSpaceSchema);
if (NULL == pNodeSOAPEnvelope) goto CleanUp;
QuitIfFail(InsertNode(pDocSOAPQuery, pNodeSOAPEnvelope));
pNodeSOAPBody = CreateDOMNode(pDocSOAPQuery, NODE_ELEMENT, bstrNameSOAPBody, bstrNameSpaceSchema);
if (NULL == pNodeSOAPBody) goto CleanUp;
QuitIfFail(InsertNode(pNodeSOAPEnvelope, pNodeSOAPBody));
pNodeGetManifest = CreateDOMNode(pDocSOAPQuery, NODE_ELEMENT, bstrNameGetManifest);
if (NULL == pNodeGetManifest) goto CleanUp;
QuitIfFail(InsertNode(pNodeSOAPBody, pNodeGetManifest));
if (NULL != pXMLClientInfo)
{
QuitIfFail(FindSingleDOMNode(pXMLClientInfo, bstrClientInfo, &pNodeClientInfo));
QuitIfFail(CopyNode(pNodeClientInfo, pDocSOAPQuery, &pNodeClientInfoNew));
QuitIfFail(InsertNode(pNodeGetManifest, pNodeClientInfoNew));
}
if (NULL != pXMLSystemSpec)
{
QuitIfFail(FindSingleDOMNode(pXMLSystemSpec, bstrSystemInfo, &pNodeSystemInfo));
QuitIfFail(CopyNode(pNodeSystemInfo, pDocSOAPQuery, &pNodeSystemInfoNew));
QuitIfFail(InsertNode(pNodeGetManifest, pNodeSystemInfoNew));
}
QuitIfFail(FindSingleDOMNode(pXMLQuery, bstrQuery, &pNodeQuery));
QuitIfFail(CopyNode(pNodeQuery, pDocSOAPQuery, &pNodeQueryNew));
QuitIfFail(InsertNode(pNodeGetManifest, pNodeQueryNew));
CleanUp:
if (FAILED(hr))
{
LOG_ErrorMsg(hr);
SafeReleaseNULL(pDocSOAPQuery);
}
SafeReleaseNULL(pNodeSOAPEnvelope);
SafeReleaseNULL(pNodeSOAPBody);
SafeReleaseNULL(pNodeGetManifest);
SafeReleaseNULL(pNodeClientInfo);
SafeReleaseNULL(pNodeClientInfoNew);
SafeReleaseNULL(pNodeSystemInfo);
SafeReleaseNULL(pNodeSystemInfoNew);
SafeReleaseNULL(pNodeQuery);
SafeReleaseNULL(pNodeQueryNew);
SysFreeString(bstrNameSOAPEnvelope);
SysFreeString(bstrNameSOAPBody);
SysFreeString(bstrNameGetManifest);
SysFreeString(bstrClientInfo);
SysFreeString(bstrSystemInfo);
SysFreeString(bstrQuery);
SafeHeapFree(pszSOAPQuerySchema);
SafeHeapFree(pszNameSpaceSchema);
SysFreeString(bstrNameSpaceSchema);
*ppSOAPQuery = pDocSOAPQuery;
return hr;
}
// Function name : ValidatePID
// Description : This function is used to check the return xml from the
// server in response to getmanifest calls.
// If the catalogStatus attribute is not present or is 0 then the pid validation succeeded
// If the catalogStatus attribute is 1 then an error is returned
// Return type : HRESULT
// Argument : IXMLDOMDocument *pXmlDomDocument
HRESULT ValidatePID(IXMLDOMDocument *pXmlDomDocument)
{
LOG_Block("ValidatePID()");
HRESULT hr = S_OK;
IXMLDOMElement *pRootElement = NULL;
long lStatus = 0;
if(!pXmlDomDocument)
{
return E_INVALIDARG;
}
BSTR bCatalogStatus = SysAllocString(L"catalogStatus");
if(!bCatalogStatus)
{
return E_OUTOFMEMORY;
}
QuitIfFail( pXmlDomDocument->get_documentElement(&pRootElement) );
//get the catalogStatus attribute
QuitIfFail( GetAttribute( (IXMLDOMNode *)pRootElement, bCatalogStatus, &lStatus));
if(errorInvalidLicense == lStatus)
hr = E_INVALID_PID;
CleanUp:
if(FAILED(hr))
LOG_ErrorMsg(hr);
//catalogStatus is an optional attribute. If it is not found we get the
//hresult as S_FALSE. So reset it to S_OK
if(S_FALSE == hr)
hr = S_OK;
SafeReleaseNULL(pRootElement);
SysFreeString(bCatalogStatus);
return hr;
}
// Function name : PingInvalidPID
// Description : This function sends a ping message to the server
// to indicate failure of PID validation
// Return type : void
// Argument : BSTR bstrClientName
// Argument : HRESULT hRes
// Argument : HANDLE *phQuit
// Argument : DWORD dwNumHandles
void PingInvalidPID(BSTR bstrClientName, HRESULT hRes, HANDLE *phQuit, DWORD dwNumHandles)
{
LOG_Block("PingInvalidPID()");
USES_IU_CONVERSION;
HRESULT hr = S_OK;
URLLOGSTATUS status = URLLOGSTATUS_Declined;
LPTSTR ptszLivePingServerUrl = NULL;
LPTSTR ptszCorpPingServerUrl = NULL;
if (NULL != (ptszLivePingServerUrl = (LPTSTR)HeapAlloc(
GetProcessHeap(),
HEAP_ZERO_MEMORY,
INTERNET_MAX_URL_LENGTH * sizeof(TCHAR))))
{
if (FAILED( g_pUrlAgent->GetLivePingServer(ptszLivePingServerUrl, INTERNET_MAX_URL_LENGTH) ) )
{
SafeHeapFree(ptszLivePingServerUrl);
}
}
else
{
LOG_Out(_T("failed to allocate memory for ptszLivePingServerUrl"));
}
if (NULL != (ptszCorpPingServerUrl = (LPTSTR)HeapAlloc(
GetProcessHeap(),
HEAP_ZERO_MEMORY,
INTERNET_MAX_URL_LENGTH * sizeof(TCHAR))))
{
if (FAILED(g_pUrlAgent->GetCorpPingServer(ptszCorpPingServerUrl, INTERNET_MAX_URL_LENGTH)))
{
LOG_Out(_T("failed to get corp WU ping server URL"));
SafeHeapFree(ptszCorpPingServerUrl);
}
}
else
{
LOG_Out(_T("failed to allocate memory for ptszCorpPingServerUrl"));
}
LPTSTR lpClientName = NULL;
if(bstrClientName)
{
lpClientName = OLE2T(bstrClientName);
}
CUrlLog pingSvr(lpClientName, ptszLivePingServerUrl, ptszCorpPingServerUrl);
SafeHeapFree(ptszLivePingServerUrl);
SafeHeapFree(ptszCorpPingServerUrl);
pingSvr.Ping(TRUE, // on-line
URLLOGDESTINATION_DEFAULT, // going live or corp WU ping server
phQuit, // pt to cancel events
dwNumHandles, // number of events
URLLOGACTIVITY_Download, // activity
status, // status code
hRes,
NULL,
NULL,
g_szInvalidPID
);
}