blob: 1fdeb98a5110469c2f91cc342dd5a27df598b468 [file] [log] [blame]
// Copyright 2007-2010 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
//
// goopdate_xml_parser for parsing the xml manifest files that are compiled
// into the meta-installer and sent by the auto-update server.
#include "omaha/goopdate/goopdate_xml_parser.h"
#include <stdlib.h>
#include <msxml2.h>
#include <vector>
#include "base/basictypes.h"
#include "omaha/common/constants.h"
#include "omaha/common/error.h"
#include "omaha/common/string.h"
#include "omaha/common/utils.h"
#include "omaha/common/xml_utils.h"
#include "omaha/goopdate/config_manager.h"
#include "omaha/goopdate/const_goopdate.h"
#include "omaha/goopdate/goopdate_metrics.h"
#include "omaha/goopdate/goopdate_utils.h"
namespace omaha {
namespace {
// Loads the specified document and obtains the DOM and Element interfaces.
HRESULT GetDocumentDomAndElement(const CString& file_name,
IXMLDOMDocument** document,
IXMLDOMElement** document_element) {
CORE_LOG(L3, (_T("[GetDocumentDomAndElement]")));
ASSERT1(document);
ASSERT1(document_element);
HRESULT hr = LoadXMLFromFile(file_name, true, document);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[LoadXMLFromFile failed][0x%x]"), hr));
return hr;
}
hr = (*document)->get_documentElement(document_element);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[get_documentElement failed][0x%x]"), hr));
return hr;
}
if (!*document_element) { // Protect against msxml S_FALSE return.
CORE_LOG(LE, (_T("[*document_element is NULL]")));
return E_FAIL;
}
return S_OK;
}
// Loads the specified document and obtains the DOM interface.
HRESULT GetDocumentElement(const CString& file_name,
IXMLDOMElement** document_element) {
CComPtr<IXMLDOMDocument> document;
return GetDocumentDomAndElement(file_name, &document, document_element);
}
} // namespace
const int kGuidLen = 38;
CString GoopdateXmlParser::seed_protocol_version;
// Constants for creating the xml request.
namespace Xml {
const TCHAR* const kHeaderText =
_T("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
const TCHAR* const kProcessingText =
_T("version=\"1.0\" encoding=\"UTF-8\"");
namespace Namespace {
const TCHAR* const kRequest = _T("http://www.google.com/update2/request");
const TCHAR* const kResponse = _T("http://www.google.com/update2/response");
const TCHAR* const kSeed = _T("http://www.google.com/update2/install");
}
namespace Element {
const TCHAR* const kXml = _T("xml");
const TCHAR* const kRequests = _T("gupdate");
const TCHAR* const kOmahaVersion = _T("updaterversion");
const TCHAR* const kOs = _T("os");
const TCHAR* const kApp = _T("app");
const TCHAR* const kUpdateCheck = _T("updatecheck");
const TCHAR* const kPing = _T("ping");
const TCHAR* const kEvent = _T("event");
const TCHAR* const kComponents = _T("components");
const TCHAR* const kComponent = _T("component");
const TCHAR* const kResponses = _T("gupdate");
const TCHAR* const kData = _T("data");
const TCHAR* const kInstall = _T("install");
const TCHAR* const kResponse = _T("response");
const TCHAR* const kNameValue = _T("attr");
const TCHAR* const kDayStart = _T("daystart");
}
namespace Attribute {
const TCHAR* const kActive = _T("active");
const TCHAR* const kAdditionalParameter = _T("ap");
const TCHAR* const kAppGuid = _T("appguid");
const TCHAR* const kApplicationName = _T("appname");
const TCHAR* const kAppId = _T("appid");
const TCHAR* const kArguments = _T("arguments");
const TCHAR* const kBrandCode = _T("brand");
const TCHAR* const kBrowserType = _T("browser");
const TCHAR* const kClientId = _T("client");
const TCHAR* const kCodebase = _T("codebase");
const TCHAR* const kCountry = _T("country");
const TCHAR* const kDaysSinceLastActivePing = _T("a");
const TCHAR* const kDaysSinceLastRollCall = _T("r");
const TCHAR* const kErrorCode = _T("errorcode");
const TCHAR* const kEventResult = _T("eventresult");
const TCHAR* const kEventType = _T("eventtype");
const TCHAR* const kErrorUrl = _T("errorurl");
const TCHAR* const kExtraCode1 = _T("extracode1");
const TCHAR* const kHash = _T("hash");
const TCHAR* const kIndex = _T("index");
const TCHAR* const kInstalledAgeDays = _T("installage");
const TCHAR* const kIsMachine = _T("ismachine");
const TCHAR* const kInstallationId = _T("iid");
const TCHAR* const kInstallSource = _T("installsource");
const TCHAR* const kLang = _T("lang");
const TCHAR* const kName = _T("name");
const TCHAR* const kNeedsAdmin = _T("needsadmin");
const TCHAR* const kParameter = _T("parameter");
const TCHAR* const kPeriodOverrideSec = _T("periodoverridesec");
const TCHAR* const kPlatform = _T("platform");
const TCHAR* const kPreviousVersion = _T("previousversion");
const TCHAR* const kProtocol = _T("protocol");
const TCHAR* const kRequestId = _T("requestid");
const TCHAR* const kServicePack = _T("sp");
const TCHAR* const kSessionId = _T("sessionid");
const TCHAR* const kSignature = _T("signature");
const TCHAR* const kSize = _T("size");
const TCHAR* const kStatus = _T("status");
const TCHAR* const kSuccessAction = _T("onsuccess");
const TCHAR* const kSuccessUrl = _T("successurl");
const TCHAR* const kTag = _T("tag");
const TCHAR* const kTestSource = _T("testsource");
const TCHAR* const kTerminateAllBrowsers = _T("terminateallbrowsers");
const TCHAR* const kTTToken = _T("tttoken");
const TCHAR* const kUpdateDisabled = _T("updatedisabled");
const TCHAR* const kVersion = _T("version");
const TCHAR* const kXmlns = _T("xmlns");
const TCHAR* const kElapsedSeconds = _T("elapsed_seconds");
}
namespace Value {
const TCHAR* const kRequestType = _T("UpdateRequest");
const TCHAR* const kProtocol = _T("2.0");
const TCHAR* const kVersion2 = _T("2.0");
const TCHAR* const kVersion3 = _T("3.0");
const TCHAR* const kTrue = _T("true");
const TCHAR* const kFalse = _T("false");
const TCHAR* const kStatusError = _T("error");
const TCHAR* const kSuccessActionDefault = _T("default");
const TCHAR* const kSuccessActionExitSilently = _T("exitsilently");
const TCHAR* const kSuccessActionExitSilentlyOnLaunchCmd =
_T("exitsilentlyonlaunchcmd");
const TCHAR* const kStatusOk = kResponseStatusOkValue;
const TCHAR* const kInstallData = _T("install");
}
}
SuccessfulInstallAction GoopdateXmlParser::ConvertStringToSuccessAction(
const CString& text) {
if (text.IsEmpty() ||
_tcsicmp(text, Xml::Value::kSuccessActionDefault) == 0) {
return SUCCESS_ACTION_DEFAULT;
} else if (_tcsicmp(text, Xml::Value::kSuccessActionExitSilently) == 0) {
return SUCCESS_ACTION_EXIT_SILENTLY;
} else if (_tcsicmp(text,
Xml::Value::kSuccessActionExitSilentlyOnLaunchCmd) == 0) {
return SUCCESS_ACTION_EXIT_SILENTLY_ON_LAUNCH_CMD;
} else {
ASSERT(false, (_T("[Unrecognized success action][%s]"), text));
// Use the default action. This allows Omaha to be forward-compatible with
// new SuccessActions, meaning older versions will not fail if a config
// uses a new action.
return SUCCESS_ACTION_DEFAULT;
}
}
// Implementation of the GoopdateXmlParser.
HRESULT GoopdateXmlParser::ReadBooleanAttribute(IXMLDOMNode* node,
const TCHAR* attr_name,
bool* value) {
CORE_LOG(L3, (_T("[ReadBooleanAttribute][%s]"), attr_name));
ASSERT1(node != NULL);
ASSERT1(attr_name != NULL);
ASSERT1(value != NULL);
CComBSTR node_value;
HRESULT hr = ReadAttribute(node, attr_name, &node_value);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[ReadAttribute failed][%s][0x%x]"), attr_name, hr));
return hr;
}
hr = String_StringToBool(static_cast<TCHAR*>(node_value),
value);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[String_StringToBool failed][0x%x]"), hr));
return hr;
}
return S_OK;
}
HRESULT GoopdateXmlParser::ReadIntAttribute(IXMLDOMNode* node,
const TCHAR* attr_name,
int* value) {
CORE_LOG(L3, (_T("[ReadIntAttribute][%s]"), attr_name));
ASSERT1(node != NULL);
ASSERT1(attr_name != NULL);
ASSERT1(value != NULL);
CComBSTR node_value;
HRESULT hr = ReadAttribute(node, attr_name, &node_value);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[ReadAttribute failed][%s][0x%x]"), attr_name, hr));
return hr;
}
if (!String_StringToDecimalIntChecked(
static_cast<const TCHAR*>(node_value), value)) {
return GOOPDATEXML_E_STRTOUINT;
}
return S_OK;
}
HRESULT GoopdateXmlParser::ReadGuidAttribute(IXMLDOMNode* node,
const TCHAR* attr_name,
GUID* value) {
CORE_LOG(L3, (_T("[ReadGuidAttribute][%s]"), attr_name));
ASSERT1(node != NULL);
ASSERT1(attr_name != NULL);
ASSERT1(value != NULL);
CComBSTR node_value;
HRESULT hr = ReadAttribute(node, attr_name, &node_value);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[ReadAttribute failed][%s][0x%x]"), attr_name, hr));
return hr;
}
hr = ::CLSIDFromString(static_cast<TCHAR*>(node_value), value);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[CLSIDFromString failed][0x%x]"), hr));
return hr;
}
return S_OK;
}
HRESULT GoopdateXmlParser::ReadStringAttribute(IXMLDOMNode* node,
const TCHAR* attr_name,
CString* value) {
CORE_LOG(L3, (_T("[ReadStringAttribute][%s]"), attr_name));
ASSERT1(node != NULL);
ASSERT1(attr_name != NULL);
ASSERT1(value != NULL);
CComBSTR node_value;
HRESULT hr = ReadAttribute(node, attr_name, &node_value);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[ReadAttribute failed][%s][0x%x]"), attr_name, hr));
return hr;
}
// Will extract the underlying string.
*value = static_cast<TCHAR*>(node_value);
return S_OK;
}
HRESULT GoopdateXmlParser::ReadAttribute(IXMLDOMNode* node,
const TCHAR* attr_name,
BSTR* value) {
CORE_LOG(L4, (_T("[ReadAttribute][%s]"), attr_name));
ASSERT1(node != NULL);
ASSERT1(attr_name != NULL);
ASSERT1(value != NULL);
// First read the attributes.
CComPtr<IXMLDOMNamedNodeMap> attributes;
HRESULT hr = node->get_attributes(&attributes);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[get_attributes failed][0x%x]"), hr));
return hr;
}
if (!attributes) {
CORE_LOG(LE, (_T("[Msxml S_FALSE return.]")));
return E_FAIL; // Protect against msxml S_FALSE return.
}
CComPtr<IXMLDOMNode> attribute_node;
CComVariant node_value;
CComBSTR temp_attr_name(attr_name);
// Get the attribute using a named node.
hr = attributes->getNamedItem(static_cast<BSTR>(temp_attr_name),
&attribute_node);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[getNamedItem failed][0x%x]"), hr));
return hr;
}
if (!attribute_node) {
CORE_LOG(LE, (_T("[Msxml S_FALSE return.]")));
return E_FAIL; // Protect against msxml S_FALSE return.
}
hr = attribute_node->get_nodeValue(&node_value);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[get_nodeValue failed][0x%x]"), hr));
return hr;
}
if (node_value.vt == VT_EMPTY) {
CORE_LOG(LE, (_T("[node_value.vt == VT_EMPTY]")));
return E_FAIL;
}
// Extract the variant into a BSTR.
node_value.CopyTo(value);
return S_OK;
}
HRESULT GoopdateXmlParser::ReadStringValue(IXMLDOMNode* node,
CString* value) {
CORE_LOG(L4, (_T("[ReadStringValue]")));
ASSERT1(node != NULL);
ASSERT1(value != NULL);
CComPtr<IXMLDOMNodeList> child_nodes;
HRESULT hr = node->get_childNodes(&child_nodes);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[get_childNodes failed][0x%x]"), hr));
return hr;
}
if (!child_nodes) {
CORE_LOG(LE, (_T("[Msxml S_FALSE return.]")));
return E_FAIL; // Protect against msxml S_FALSE return.
}
long count = 0; // NOLINT
hr = child_nodes->get_length(&count);
if (FAILED(hr)) {
return hr;
}
ASSERT(count == 1, (_T("count: %u"), count));
CComPtr<IXMLDOMNode> child_node;
hr = child_nodes->nextNode(&child_node);
if (FAILED(hr)) {
return hr;
}
DOMNodeType type = NODE_INVALID;
hr = child_node->get_nodeType(&type);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[get_nodeType failed][0x%x]"), hr));
return hr;
}
if (type != NODE_TEXT) {
CORE_LOG(LE, (_T("[Invalid nodeType][%d]"), type));
return E_INVALIDARG;
}
CComVariant node_value;
hr = child_node->get_nodeValue(&node_value);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[get_nodeValue failed][0x%x]"), hr));
return hr;
}
if (node_value.vt != VT_BSTR) {
CORE_LOG(LE, (_T("[node_value.vt != VT_BSTR][%d]"), node_value.vt));
return E_INVALIDARG;
}
*value = V_BSTR(&node_value);
return S_OK;
}
HRESULT GoopdateXmlParser::ReadUpdateResponse(IXMLDOMNode* node,
UpdateResponse* response) {
CORE_LOG(L4, (_T("[ReadUpdateResponse]")));
ASSERT1(node != NULL);
ASSERT1(response != NULL);
UpdateResponseData response_data;
// Read GUID first since we need the GUID even on errors in order
// to remove the corresponding request from the jobs list.
GUID guid = {0};
HRESULT hr = ReadGuidAttribute(node, Xml::Attribute::kAppId, &guid);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[ReadGuidAttribute failed][0x%x]"), hr));
return hr;
}
response_data.set_guid(guid);
CString str;
// Any response status but "ok" for the "app" element stops the "parsing".
hr = ReadStringAttribute(node, Xml::Attribute::kStatus, &str);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[ReadStringAttribute failed][0x%x]"), hr));
return hr;
}
if (str != Xml::Value::kStatusOk) {
response_data.set_status(str);
response->set_update_response_data(response_data);
return S_OK;
}
// Now try and read the children nodes of the response.
CComPtr<IXMLDOMNodeList> child_nodes;
// Get all the children of the document.
hr = node->get_childNodes(&child_nodes);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[get_childNodes failed][0x%x]"), hr));
return hr;
}
if (!child_nodes) {
CORE_LOG(LE, (_T("[Msxml S_FALSE return.]")));
return E_FAIL; // Protect against msxml S_FALSE return.
}
// Go over all the children and handle the children. We ignore all the
// children that we do not understand. Note that we expect the nodes that
// we want to be present. Although we are not enforcing this for now.
CComPtr<IXMLDOMNode> child_node;
while (child_nodes->nextNode(&child_node) != S_FALSE) {
XMLFQName node_name;
hr = GetXMLFQName(child_node, &node_name);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[GetXMLFQName failed][0x%x]"), hr));
return hr;
}
if (node_name.base == Xml::Element::kUpdateCheck) {
hr = ReadStringAttribute(child_node, Xml::Attribute::kTTToken, &str);
if (SUCCEEDED(hr) && !str.IsEmpty()) {
response_data.set_tt_token(str);
}
hr = ReadStringAttribute(child_node, Xml::Attribute::kStatus, &str);
if (FAILED(hr)) { return hr; }
response_data.set_status(str);
if (str == Xml::Value::kStatusOk) {
int size = 0;
hr = ReadIntAttribute(child_node, Xml::Attribute::kSize, &size);
if (FAILED(hr)) { return hr; }
if (size < 0) {
return GOOPDATEXML_E_STRTOUINT;
}
response_data.set_size(size);
hr = ReadStringAttribute(child_node, Xml::Attribute::kHash, &str);
if (FAILED(hr)) { return hr; }
response_data.set_hash(str);
hr = ReadStringAttribute(child_node, Xml::Attribute::kCodebase, &str);
if (FAILED(hr)) { return hr; }
response_data.set_url(str);
hr = ReadStringAttribute(child_node, Xml::Attribute::kNeedsAdmin, &str);
if (FAILED(hr)) { return hr; }
NeedsAdmin needs_admin;
hr = goopdate_utils::ConvertStringToNeedsAdmin(str, &needs_admin);
if (FAILED(hr)) { return hr; }
response_data.set_needs_admin(needs_admin);
hr = ReadStringAttribute(child_node, Xml::Attribute::kArguments, &str);
// arguments is optional
if (SUCCEEDED(hr)) {
response_data.set_arguments(str);
}
hr = ReadStringAttribute(child_node, Xml::Attribute::kSuccessUrl, &str);
if (SUCCEEDED(hr)) {
response_data.set_success_url(str);
}
bool terminate_all_browsers = false;
hr = ReadBooleanAttribute(child_node,
Xml::Attribute::kTerminateAllBrowsers,
&terminate_all_browsers);
if (SUCCEEDED(hr)) {
response_data.set_terminate_all_browsers(terminate_all_browsers);
}
hr = ReadStringAttribute(child_node,
Xml::Attribute::kSuccessAction,
&str);
if (SUCCEEDED(hr)) {
response_data.set_success_action(ConvertStringToSuccessAction(str));
}
// If no version exists in the server response, we will still indicate
// when an update is available. However, we will not provide version
// information to any JobObserver.
hr = ReadStringAttribute(child_node,
Xml::Attribute::kVersion,
&str);
if (SUCCEEDED(hr)) {
response_data.set_version(str);
}
}
// Always look for the error URL because it is used in errors.
hr = ReadStringAttribute(child_node, Xml::Attribute::kErrorUrl, &str);
if (SUCCEEDED(hr)) {
response_data.set_error_url(str);
}
} else if (node_name.base == Xml::Element::kData) {
hr = ReadStringAttribute(child_node, Xml::Attribute::kStatus, &str);
if (FAILED(hr)) {
return hr;
}
if (str == Xml::Value::kStatusOk) {
// <data name="install" index="foo" status="ok">foo bar</data>.
hr = ReadStringAttribute(child_node, Xml::Attribute::kName, &str);
if (FAILED(hr)) {
return hr;
}
if (str.CompareNoCase(Xml::Value::kInstallData)) {
CORE_LOG(LW, (_T("[Skipping unsupported data][%s]"), str));
continue;
}
CString install_data_index;
hr = ReadStringAttribute(child_node,
Xml::Attribute::kIndex,
&install_data_index);
if (FAILED(hr)) {
return hr;
}
CString install_data;
hr = ReadStringValue(child_node, &install_data);
if (FAILED(hr)) {
return hr;
}
response_data.SetInstallData(install_data_index, install_data);
}
} else if (node_name.base == Xml::Element::kPing) {
// nothing to do here (yet)
} else if (node_name.base == Xml::Element::kEvent) {
// nothing to do here (yet)
} else if (node_name.base == Xml::Element::kComponents) {
VERIFY1(SUCCEEDED(ReadComponentsResponses(child_node, response)));
}
child_node = NULL;
}
response->set_update_response_data(response_data);
return S_OK;
}
HRESULT GoopdateXmlParser::ReadComponentsResponses(IXMLDOMNode* node,
UpdateResponse* response) {
CORE_LOG(L4, (_T("[ReadComponentsResponses]")));
ASSERT1(node);
ASSERT1(response);
CComPtr<IXMLDOMNodeList> child_nodes;
HRESULT hr = node->get_childNodes(&child_nodes);
if (FAILED(hr)) { return hr; }
// If S_FALSE, then there are no children components.
if (S_FALSE == hr) { return S_OK; }
CComPtr<IXMLDOMNode> child_node;
while (child_nodes->nextNode(&child_node) != S_FALSE) {
XMLFQName node_name;
hr = GetXMLFQName(child_node, &node_name);
if (FAILED(hr)) { return hr; }
if (node_name.base == Xml::Element::kComponent) {
UpdateResponseData component_response_data;
hr = ReadComponentResponseData(child_node, &component_response_data);
if (FAILED(hr)) { return hr; }
response->AddComponentResponseData(component_response_data);
} else {
ASSERT1(false);
}
child_node = NULL;
}
return S_OK;
}
HRESULT GoopdateXmlParser::ReadComponentResponseData(
IXMLDOMNode* node,
UpdateResponseData* response_data) {
CORE_LOG(L4, (_T("[ReadComponentsResponseData]")));
// TODO(omaha): consolidate component/product parsing. They have several
// similar attributes.
// Read GUID first since we need the GUID even on errors in order to remove
// the corresponding request from the jobs list.
GUID guid = {0};
HRESULT hr = ReadGuidAttribute(node, Xml::Attribute::kAppId, &guid);
if (FAILED(hr)) { return hr; }
response_data->set_guid(guid);
// Any response status but "ok" for the "app" element stops the "parsing".
CString str;
hr = ReadStringAttribute(node, Xml::Attribute::kStatus, &str);
if (FAILED(hr)) { return hr; }
if (str != Xml::Value::kStatusOk) {
response_data->set_status(str);
return S_OK;
}
// Now try and read the children nodes of the response.
CComPtr<IXMLDOMNodeList> child_nodes;
// Get all the children of the document.
hr = node->get_childNodes(&child_nodes);
if (FAILED(hr)) { return hr; }
if (!child_nodes) { return E_FAIL; } // Protect against msxml S_FALSE return.
// Go over all the children and handle the children. We ignore all the
// children that we do not understand. Note that we expect the nodes that
// we want to be present. Although we are not enforcing this for now.
CComPtr<IXMLDOMNode> child_node;
while (child_nodes->nextNode(&child_node) != S_FALSE) {
XMLFQName node_name;
hr = GetXMLFQName(child_node, &node_name);
if (FAILED(hr)) { return hr; }
if (node_name.base == Xml::Element::kUpdateCheck) {
hr = ReadStringAttribute(child_node, Xml::Attribute::kStatus, &str);
if (FAILED(hr)) { return hr; }
response_data->set_status(str);
// TODO(omaha): Check why we have kStatusOk at both levels of the XML
// (same for product parsing) and consolidate/remove as necessary.
if (str == Xml::Value::kStatusOk) {
int size = 0;
hr = ReadIntAttribute(child_node, Xml::Attribute::kSize, &size);
if (FAILED(hr)) { return hr; }
if (size < 0) {
return GOOPDATEXML_E_STRTOUINT;
}
response_data->set_size(size);
hr = ReadStringAttribute(child_node, Xml::Attribute::kHash, &str);
if (FAILED(hr)) { return hr; }
response_data->set_hash(str);
hr = ReadStringAttribute(child_node, Xml::Attribute::kCodebase, &str);
if (FAILED(hr)) { return hr; }
response_data->set_url(str);
hr = ReadStringAttribute(child_node, Xml::Attribute::kArguments, &str);
// arguments is optional
if (SUCCEEDED(hr)) {
response_data->set_arguments(str);
}
}
} else {
ASSERT1(false);
}
child_node = NULL;
}
return S_OK;
}
// This exists only to support handoffs from 1.0.x and 1.1.x metainstallers.
HRESULT GoopdateXmlParser::ReadInstallElement(
IXMLDOMNode* node,
UpdateResponseData* response_data) {
ASSERT1(node);
ASSERT1(response_data);
HRESULT hr = ReadRequiredInstallAttributes(node, response_data);
if (FAILED(hr)) {
return hr;
}
return ReadOptionalInstallAttributes(node, response_data);
}
// This exists only to support handoffs from 1.0.x and 1.1.x metainstallers.
HRESULT GoopdateXmlParser::ReadRequiredInstallAttributes(
IXMLDOMNode* node,
UpdateResponseData* response_data) {
ASSERT1(node);
ASSERT1(response_data);
// Read the guid and whether the application needs admin. Both the attributes
// are required.
GUID guid_value = {0};
HRESULT hr = ReadGuidAttribute(node, Xml::Attribute::kAppGuid,
&guid_value);
if (FAILED(hr)) { return hr; }
response_data->set_guid(guid_value);
CString needs_admin_str;
hr = ReadStringAttribute(node, Xml::Attribute::kNeedsAdmin,
&needs_admin_str);
if (FAILED(hr)) { return hr; }
NeedsAdmin needs_admin;
hr = goopdate_utils::ConvertStringToNeedsAdmin(needs_admin_str, &needs_admin);
if (FAILED(hr)) { return hr; }
response_data->set_needs_admin(needs_admin);
// We only read the language and the application name from the seed manifest
// if the version of the seed manifest is greater than 2.0.
if (String_StringToDouble(seed_protocol_version) >
String_StringToDouble(Xml::Value::kVersion2)) {
++metric_handoff_legacy_11;
CString app_name;
hr = ReadStringAttribute(node, Xml::Attribute::kApplicationName, &app_name);
if (FAILED(hr)) { return hr; }
response_data->set_app_name(app_name);
CString language;
hr = ReadStringAttribute(node, Xml::Attribute::kLang, &language);
if (FAILED(hr)) { return hr; }
response_data->set_language(language);
} else {
++metric_handoff_legacy_10;
}
return S_OK;
}
// Since all of these attributes are optional, read failures do not cause this
// method to fail.
// This exists only to support handoffs from 1.0.x and 1.1.x metainstallers.
HRESULT GoopdateXmlParser::ReadOptionalInstallAttributes(
IXMLDOMNode* node,
UpdateResponseData* response_data) {
ASSERT1(node);
ASSERT1(response_data);
CString iid;
HRESULT hr = ReadStringAttribute(node, Xml::Attribute::kInstallationId, &iid);
if (SUCCEEDED(hr)) {
response_data->set_installation_id(StringToGuid(iid));
}
CString ap;
hr = ReadStringAttribute(node, Xml::Attribute::kAdditionalParameter, &ap);
if (SUCCEEDED(hr)) {
response_data->set_ap(ap);
}
CString browser_type;
hr = ReadStringAttribute(node, Xml::Attribute::kBrowserType, &browser_type);
if (SUCCEEDED(hr)) {
BrowserType type = BROWSER_UNKNOWN;
hr = goopdate_utils::ConvertStringToBrowserType(browser_type, &type);
if (SUCCEEDED(hr)) {
response_data->set_browser_type(type);
}
}
return S_OK;
}
// Assumes all higher level validity checks have been done
HRESULT GoopdateXmlParser::ReadUpdateResponses(
IXMLDOMNode* node,
UpdateResponses* responses) {
ASSERT1(node != NULL);
ASSERT1(responses != NULL);
int time_since_midnight_sec(0);
HRESULT hr = ReadTimeSinceMidnightSec(node, &time_since_midnight_sec);
if (FAILED(hr)) { return hr; }
CComPtr<IXMLDOMNodeList> child_nodes;
// Get all the children of the Node.
hr = node->get_childNodes(&child_nodes);
if (FAILED(hr)) { return hr; }
if (!child_nodes) { return E_FAIL; } // Protect against msxml S_FALSE return.
// Go Over all the children and read each of them. we will ignore ones that
// we dont understand.
hr = child_nodes->reset();
if (FAILED(hr)) { return hr; }
CComPtr<IXMLDOMNode> child_node;
while (child_nodes->nextNode(&child_node) != S_FALSE) {
XMLFQName child_node_name;
hr = GetXMLFQName(child_node, &child_node_name);
if (FAILED(hr)) { return hr; }
if (child_node_name.base == Xml::Element::kApp) {
// we got a response we should read that in.
UpdateResponse response;
response.set_time_since_midnight_sec(time_since_midnight_sec);
hr = ReadUpdateResponse(child_node, &response);
if (FAILED(hr)) { return hr; }
const GUID& response_guid = response.update_response_data().guid();
ASSERT1(responses->find(response_guid) == responses->end());
(*responses)[response_guid] = response;
}
child_node = NULL;
}
return S_OK;
}
// This exists only to support handoffs from 1.0.x and 1.1.x metainstallers.
HRESULT GoopdateXmlParser::ReadSeedInstalls(IXMLDOMNode* node,
UpdateResponses* responses) {
ASSERT1(node != NULL);
ASSERT1(responses != NULL);
CComPtr<IXMLDOMNodeList> child_nodes;
// Get all the children of the Node.
HRESULT hr = node->get_childNodes(&child_nodes);
if (FAILED(hr)) { return hr; }
if (!child_nodes) { return E_FAIL; } // Protect against msxml S_FALSE return.
// Go Over all the children and read each of them. Since seed files are
// always of a version <= to this code, error if we run into a child we don't
// recognize.
hr = child_nodes->reset();
if (FAILED(hr)) { return hr; }
CComPtr<IXMLDOMNode> child_node;
while (child_nodes->nextNode(&child_node) != S_FALSE) {
XMLFQName child_node_name;
hr = GetXMLFQName(child_node, &child_node_name);
if (FAILED(hr)) { return hr; }
if (child_node_name.base == Xml::Element::kInstall) {
// We found an install node. We should read that in.
UpdateResponseData response_data;
hr = ReadInstallElement(child_node, &response_data);
if (FAILED(hr)) { return hr; }
UpdateResponse response(response_data);
const GUID& response_guid = response.update_response_data().guid();
ASSERT1(responses->find(response_guid) == responses->end());
(*responses)[response_guid] = response;
child_node = NULL;
} else {
child_node = NULL;
return E_FAIL;
}
}
return S_OK;
}
HRESULT GoopdateXmlParser::GetProtocolVersion(IXMLDOMElement* document_element,
CString* version) {
ASSERT1(document_element);
ASSERT1(version);
CString protocol_version;
return ReadStringAttribute(document_element,
Xml::Attribute::kProtocol,
version);
}
HRESULT GoopdateXmlParser::VerifyElementProtocolCompatibility(
IXMLDOMElement* document_element,
const CString& expected_version) {
ASSERT1(document_element);
CString protocol_version;
HRESULT hr = GetProtocolVersion(document_element, &protocol_version);
if (FAILED(hr)) {
return hr;
}
return VerifyProtocolCompatibility(protocol_version, expected_version);
}
// Returns GOOPDATEXML_E_XMLVERSION when the actual_ver is not between
// the start_ver and the end_ver, both inclusive.
HRESULT GoopdateXmlParser::VerifyProtocolRange(const CString& actual_ver,
const CString& start_ver,
const CString& end_ver) {
const double version = String_StringToDouble(actual_ver);
const double start = String_StringToDouble(start_ver);
const double end = String_StringToDouble(end_ver);
if (version < start || version > end) {
return GOOPDATEXML_E_XMLVERSION;
}
return S_OK;
}
// Verify that the protocol version is one we understand. We accept
// all version numbers where major version is the same as kExpectedVersion
// which are greater than or equal to kExpectedVersion. In other words,
// we handle future minor version number increases which should be
// compatible.
HRESULT GoopdateXmlParser::VerifyProtocolCompatibility(
const CString& actual_version,
const CString& expected_version) {
if (_tcscmp(actual_version, expected_version) != 0) {
const double version = String_StringToDouble(actual_version);
const double expected = String_StringToDouble(expected_version);
if (expected > version) {
return GOOPDATEXML_E_XMLVERSION;
}
const int version_major = static_cast<int>(version);
const int expected_major = static_cast<int>(expected);
if (version_major != expected_major) {
return GOOPDATEXML_E_XMLVERSION;
}
}
return S_OK;
}
// Currently, this method only validates the name and protocol version.
HRESULT GoopdateXmlParser::ValidateResponseElement(
IXMLDOMElement* document_element) {
CComBSTR root_name;
HRESULT hr = document_element->get_baseName(&root_name);
if (FAILED(hr)) { return hr; }
XMLFQName doc_element_name(Xml::Namespace::kResponse, root_name);
if (doc_element_name.base != Xml::Element::kResponses) {
return GOOPDATEXML_E_RESPONSESNODE;
}
return VerifyElementProtocolCompatibility(document_element,
Xml::Value::kVersion2);
}
// When the server sends the update response back to client, it includes an
// XML elements as <daystart elapsed_seconds='###'> where ### is the number
// of seconds since the midnight on server. The client then uses this number to
// figure out the corresponding local UTC time. This local time stamp is used
// to determine whether a following ping need to be sent or postponed. Basically
// client should not send the same type of ping in each server day separated by
// server's midnight.
// Since the clock time used on client side is relative value, it doesn't
// matter if the client's clock is off. But if it's clock runs slower or
// faster, it may affect the calculation, esp. when server and client don't
// have time sync-ed for a long time.
HRESULT GoopdateXmlParser::ReadTimeSinceMidnightSec(IXMLDOMNode* node,
int* time_since_midnight_sec) {
ASSERT1(node != NULL);
ASSERT1(time_since_midnight_sec != NULL);
CComQIPtr<IXMLDOMElement> manifest = node;
CComPtr<IXMLDOMNodeList> child_nodes;
CComBSTR target_node_name(Xml::Element::kDayStart);
HRESULT hr = manifest->getElementsByTagName(
static_cast<BSTR>(target_node_name),
&child_nodes);
if (FAILED(hr)) { return hr; }
if (!child_nodes) { return E_FAIL; } // Protect against msxml S_FALSE return.
long num_elements(0); // NOLINT
hr = child_nodes->get_length(&num_elements);
if (FAILED(hr)) { return hr; }
if (num_elements == 0) {
return S_FALSE;
} else if (num_elements != 1) {
CORE_LOG(LE,
(_T("[ReadTimeSinceMidnightSec sees %d element(s) with name %s]"),
num_elements, Xml::Element::kDayStart));
return E_FAIL;
}
hr = child_nodes->reset();
if (FAILED(hr)) { return hr; }
CComPtr<IXMLDOMNode> child_node;
hr = child_nodes->get_item(0, &child_node);
if (FAILED(hr)) { return hr; }
*time_since_midnight_sec = 0;
hr = ReadIntAttribute(child_node,
Xml::Attribute::kElapsedSeconds,
time_since_midnight_sec);
if (FAILED(hr)) {
CORE_LOG(LE,
(_T("[ReadTimeSinceMidnightSec: read attribute %s failed][0x%x]"),
Xml::Attribute::kElapsedSeconds, hr));
return hr;
}
if (*time_since_midnight_sec < 0 ||
*time_since_midnight_sec >= kMaxTimeSinceMidnightSec) {
CORE_LOG(LE,
(_T("[ReadTimeSinceMidnightSec: attribute %s value invalid: %d]"),
Xml::Attribute::kElapsedSeconds, *time_since_midnight_sec));
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
}
return S_OK;
}
HRESULT GoopdateXmlParser::ParseManifestFile(const CString& file_name,
UpdateResponses* responses) {
if (responses == NULL) {
return E_INVALIDARG;
}
CComPtr<IXMLDOMElement> document_element;
HRESULT hr = GetDocumentElement(file_name, &document_element);
if (FAILED(hr)) { return hr; }
return ParseManifest(document_element, responses);
}
HRESULT GoopdateXmlParser::ParseManifestBytes(
const std::vector<byte>& manifest_bytes,
UpdateResponses* responses) {
ASSERT1(responses);
CComPtr<IXMLDOMDocument> document;
HRESULT hr = LoadXMLFromRawData(manifest_bytes, false, &document);
if (FAILED(hr)) { return hr; }
CComPtr<IXMLDOMElement> document_element;
hr = document->get_documentElement(&document_element);
if (FAILED(hr)) { return hr; }
if (!document_element) { // Protect against msxml S_FALSE return.
return E_FAIL;
}
return ParseManifest(document_element, responses);
}
HRESULT GoopdateXmlParser::ParseManifest(IXMLDOMElement* manifest,
UpdateResponses* responses) {
ASSERT1(manifest);
ASSERT1(responses);
CComBSTR uri;
HRESULT hr = manifest->get_namespaceURI(&uri);
if (FAILED(hr)) { return hr; }
if (uri == Xml::Namespace::kResponse) {
hr = ValidateResponseElement(manifest);
if (FAILED(hr)) { return hr; }
hr = ReadUpdateResponses(manifest, responses);
if (FAILED(hr)) { return hr; }
} else if (uri == Xml::Namespace::kSeed) {
// TODO(omaha): We should probably verify the name too.
hr = GetProtocolVersion(manifest, &seed_protocol_version);
if (FAILED(hr)) { return hr; }
hr = VerifyProtocolRange(seed_protocol_version,
Xml::Value::kVersion2,
Xml::Value::kVersion3);
if (FAILED(hr)) { return hr; }
hr = ReadSeedInstalls(manifest, responses);
if (FAILED(hr)) { return hr; }
} else {
return GOOPDATEXML_E_UNEXPECTED_URI;
}
return S_OK;
}
HRESULT GoopdateXmlParser::CreateRequestElement(
const TCHAR* xml_element_name,
const CString& value,
IXMLDOMDocument* document,
IXMLDOMNode** request_element) {
ASSERT1(xml_element_name != NULL);
ASSERT1(document != NULL);
ASSERT1(request_element != NULL);
// request element names get o: prepended to avoid a size explosion where
// the namespace attribute gets automatically added to every element by
// msxml
CString name;
name.Format(_T("o:%s"), xml_element_name);
HRESULT hr = CreateXMLNode(document,
NODE_ELEMENT,
name,
Xml::Namespace::kRequest,
value,
request_element);
return hr;
}
HRESULT GoopdateXmlParser::CreateAndAddRequestElement(
const TCHAR* xml_element_name,
const CString& value,
IXMLDOMDocument* document,
IXMLDOMNode* parent) {
ASSERT1(xml_element_name != NULL);
ASSERT1(document != NULL);
ASSERT1(parent != NULL);
CComPtr<IXMLDOMNode> element;
HRESULT hr = CreateRequestElement(xml_element_name,
value,
document,
&element);
if (FAILED(hr)) { return hr; }
hr = parent->appendChild(element, NULL);
if (FAILED(hr)) { return hr; }
return S_OK;
}
HRESULT GoopdateXmlParser::AddAttributesToNode(
const CString& namespace_uri,
const std::vector<XMLNameValuePair>& attributes,
IXMLDOMNode* element) {
ASSERT1(element != NULL);
HRESULT hr;
for (size_t i = 0; i < attributes.size(); ++i) {
hr = AddXMLAttributeNode(element,
namespace_uri,
attributes[i].first,
attributes[i].second);
if (FAILED(hr)) { return hr; }
}
return S_OK;
}
HRESULT GoopdateXmlParser::CreateAppRequestElementHelper(
const AppRequest& app_request,
IXMLDOMDocument* document,
IXMLDOMNode** app_element) {
ASSERT1(app_element);
ASSERT1(document);
const AppData& app_data = app_request.request_data().app_data();
// Create the request node.
HRESULT hr = CreateRequestElement(Xml::Element::kApp,
_T(""),
document,
app_element);
if (FAILED(hr)) { return hr; }
// Add the appid to the app node.
// appid="" // The application Id.
const int guid_len = kGuidLen;
TCHAR guid_str[guid_len + 1] = { _T('\0') };
if (StringFromGUID2(app_data.app_guid(), guid_str, guid_len + 1) <= 0) {
return E_FAIL;
}
if (FAILED(hr)) { return hr; }
// Saves about 600 bytes of code size by creating and destroying the
// array inside of the statement block.
{
// Create the list of attributes and the values that need to be added.
const XMLNameValuePair elements[] = {
std::make_pair(Xml::Attribute::kAppId, guid_str),
std::make_pair(Xml::Attribute::kVersion, app_data.version()),
std::make_pair(Xml::Attribute::kLang, app_data.language()),
std::make_pair(Xml::Attribute::kBrandCode, app_data.brand_code()),
std::make_pair(Xml::Attribute::kClientId, app_data.client_id())
};
for (size_t i = 0; i < arraysize(elements); ++i) {
hr = AddXMLAttributeNode(*app_element,
Xml::Namespace::kRequest,
elements[i].first,
elements[i].second);
if (FAILED(hr)) { return hr; }
}
}
// 0 seconds indicates unknown install time. A new install uses -1 days.
if (0 != app_data.install_time_diff_sec()) {
const int installed_full_days =
static_cast<int>(app_data.install_time_diff_sec()) / kSecondsPerDay;
ASSERT1(installed_full_days >= 0 || -1 == installed_full_days);
hr = AddXMLAttributeNode(*app_element,
Xml::Namespace::kRequest,
Xml::Attribute::kInstalledAgeDays,
itostr(installed_full_days));
if (FAILED(hr)) { return hr; }
}
if (GUID_NULL != app_data.iid()) {
hr = AddXMLAttributeNode(*app_element,
Xml::Namespace::kRequest,
Xml::Attribute::kInstallationId,
GuidToString(app_data.iid()));
if (FAILED(hr)) { return hr; }
}
if (!app_data.install_source().IsEmpty()) {
hr = AddXMLAttributeNode(*app_element,
Xml::Namespace::kRequest,
Xml::Attribute::kInstallSource,
app_data.install_source());
if (FAILED(hr)) { return hr; }
}
// Create and add component elements to the app element.
if (app_request.num_components() > 0) {
CComPtr<IXMLDOMNode> components_node;
hr = CreateRequestElement(Xml::Element::kComponents,
_T(""),
document,
&components_node);
if (FAILED(hr)) { return hr; }
hr = (*app_element)->appendChild(components_node, NULL);
if (FAILED(hr)) { return hr; }
AppRequestDataVector::const_iterator it;
for (it = app_request.components_begin();
it != app_request.components_end();
++it) {
const AppRequestData& component_request_data = *it;
const AppData& component_app_data = component_request_data.app_data();
CComPtr<IXMLDOMNode> component_node;
hr = CreateRequestElement(Xml::Element::kComponent,
_T(""),
document,
&component_node);
if (FAILED(hr)) { return hr; }
hr = AddXMLAttributeNode(component_node,
Xml::Namespace::kRequest,
Xml::Attribute::kAppId,
GuidToString(component_app_data.app_guid()));
if (FAILED(hr)) { return hr; }
hr = AddXMLAttributeNode(component_node,
Xml::Namespace::kRequest,
Xml::Attribute::kVersion,
component_app_data.version());
if (FAILED(hr)) { return hr; }
hr = components_node->appendChild(component_node, NULL);
if (FAILED(hr)) { return hr; }
// TODO(omaha): Create and add event elements to the component element
// by traversing the ping_events within component_request_data.
// Probably want to make the PingEvent traversal from below into a
// separate function.
}
}
return S_OK;
}
HRESULT GoopdateXmlParser::CreateUpdateAppRequestElement(
const AppRequest& app_request,
IXMLDOMDocument* document,
IXMLDOMNode** app_element) {
HRESULT hr = CreateAppRequestElementHelper(app_request,
document,
app_element);
if (FAILED(hr)) {
return hr;
}
const AppData& app_data = app_request.request_data().app_data();
// update check element
CComPtr<IXMLDOMNode> update_check;
hr = CreateRequestElement(Xml::Element::kUpdateCheck,
_T(""),
document,
&update_check);
if (FAILED(hr)) { return hr; }
if (app_data.is_update_disabled()) {
hr = AddXMLAttributeNode(update_check,
Xml::Namespace::kRequest,
Xml::Attribute::kUpdateDisabled,
Xml::Value::kTrue);
if (FAILED(hr)) { return hr; }
}
// tag is optional
if (!app_data.ap().IsEmpty()) {
hr = AddXMLAttributeNode(update_check,
Xml::Namespace::kRequest,
Xml::Attribute::kTag,
app_data.ap());
if (FAILED(hr)) { return hr; }
}
// Add the Trusted Tester token under the "updatecheck" element.
if (!app_data.tt_token().IsEmpty()) {
hr = AddXMLAttributeNode(update_check,
Xml::Namespace::kRequest,
Xml::Attribute::kTTToken,
app_data.tt_token());
if (FAILED(hr)) { return hr; }
}
hr = (*app_element)->appendChild(update_check, NULL);
if (FAILED(hr)) { return hr; }
if (!app_data.install_data_index().IsEmpty()) {
// data element.
CComPtr<IXMLDOMNode> install_data;
hr = CreateRequestElement(Xml::Element::kData,
_T(""),
document,
&install_data);
if (FAILED(hr)) { return hr; }
hr = AddXMLAttributeNode(install_data,
Xml::Namespace::kRequest,
Xml::Attribute::kName,
Xml::Value::kInstallData);
if (FAILED(hr)) { return hr; }
hr = AddXMLAttributeNode(install_data,
Xml::Namespace::kRequest,
Xml::Attribute::kIndex,
app_data.install_data_index());
if (FAILED(hr)) { return hr; }
hr = (*app_element)->appendChild(install_data, NULL);
if (FAILED(hr)) { return hr; }
}
bool was_active = app_data.did_run() == AppData::ACTIVE_RUN;
bool need_active = app_data.did_run() != AppData::ACTIVE_UNKNOWN;
bool has_sent_a_today = app_data.days_since_last_active_ping() == 0;
bool need_a = was_active && !has_sent_a_today;
bool need_r = app_data.days_since_last_roll_call() != 0;
if (need_active || need_a || need_r) {
// didrun element. The server calls it "ping" for legacy reasons.
CComPtr<IXMLDOMNode> ping;
hr = CreateRequestElement(Xml::Element::kPing,
_T(""),
document,
&ping);
if (FAILED(hr)) { return hr; }
// TODO(omaha): Remove "active" attribute after transition.
if (need_active) {
const TCHAR* active_str(was_active ? _T("1") : _T("0"));
hr = AddXMLAttributeNode(ping,
Xml::Namespace::kRequest,
Xml::Attribute::kActive,
active_str);
if (FAILED(hr)) { return hr; }
}
if (need_a) {
hr = AddXMLAttributeNode(ping,
Xml::Namespace::kRequest,
Xml::Attribute::kDaysSinceLastActivePing,
itostr(app_data.days_since_last_active_ping()));
if (FAILED(hr)) { return hr; }
}
if (need_r) {
hr = AddXMLAttributeNode(ping,
Xml::Namespace::kRequest,
Xml::Attribute::kDaysSinceLastRollCall,
itostr(app_data.days_since_last_roll_call()));
if (FAILED(hr)) { return hr; }
}
hr = (*app_element)->appendChild(ping, NULL);
if (FAILED(hr)) { return hr; }
}
return S_OK;
}
HRESULT GoopdateXmlParser::CreatePingAppRequestElement(
const AppRequest& app_request,
IXMLDOMDocument* document,
IXMLDOMNode** app_element) {
HRESULT hr = CreateAppRequestElementHelper(app_request,
document,
app_element);
if (FAILED(hr)) {
return hr;
}
// create and add Ping elements to the app element. Server calls ping elements
// "event" elements for legacy reasons.
std::vector<PingEvent>::const_iterator it;
for (it = app_request.request_data().ping_events_begin();
it != app_request.request_data().ping_events_end();
++it) {
const PingEvent& app_event = *it;
CComPtr<IXMLDOMNode> ev;
hr = CreateRequestElement(Xml::Element::kEvent,
_T(""),
document,
&ev);
if (FAILED(hr)) { return hr; }
CString str;
str.Format(_T("%d"), app_event.event_type());
hr = AddXMLAttributeNode(ev,
Xml::Namespace::kRequest,
Xml::Attribute::kEventType,
str);
if (FAILED(hr)) { return hr; }
str.Format(_T("%d"), app_event.event_result());
hr = AddXMLAttributeNode(ev,
Xml::Namespace::kRequest,
Xml::Attribute::kEventResult,
str);
if (FAILED(hr)) { return hr; }
str.Format(_T("%d"), app_event.error_code());
hr = AddXMLAttributeNode(ev,
Xml::Namespace::kRequest,
Xml::Attribute::kErrorCode,
str);
if (FAILED(hr)) { return hr; }
str.Format(_T("%d"), app_event.extra_code1());
hr = AddXMLAttributeNode(ev,
Xml::Namespace::kRequest,
Xml::Attribute::kExtraCode1,
str);
if (FAILED(hr)) { return hr; }
str = app_event.previous_version();
if (!str.IsEmpty()) {
hr = AddXMLAttributeNode(ev,
Xml::Namespace::kRequest,
Xml::Attribute::kPreviousVersion,
str);
if (FAILED(hr)) { return hr; }
}
hr = (*app_element)->appendChild(ev, NULL);
if (FAILED(hr)) { return hr; }
}
return S_OK;
}
HRESULT GoopdateXmlParser::GenerateRequest(const Request& req,
bool is_update_check,
CString* request_buffer) {
CORE_LOG(L3, (_T("[GenerateRequest]")));
ASSERT1(request_buffer);
if (!request_buffer) {
return E_INVALIDARG;
}
// Create the XML document.
CComPtr<IXMLDOMDocument> document;
HRESULT hr = CoCreateSafeDOMDocument(&document);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[CoCreateSafeDOMDocument failed][0x%x]"), hr));
return hr;
}
// Create the AppRequests Element.
CComPtr<IXMLDOMNode> update_requests;
hr = CreateRequestElement(Xml::Element::kRequests,
_T(""),
document,
&update_requests);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[CreateRequestElement failed][0x%x]"), hr));
return hr;
}
// Add the update requests node to the document.
CComPtr<IXMLDOMElement> update_requests_node;
hr = update_requests->QueryInterface(&update_requests_node);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[update_requests->QueryInterface][0x%x]"), hr));
return hr;
}
hr = document->putref_documentElement(update_requests_node);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[putref_documentElement failed][0x%x]"), hr));
return hr;
}
// add attributes to the top element:
// * protocol - protocol version
// * version - omaha version
// * testsource - test source
// * requestid - Unique request id
hr = AddXMLAttributeNode(update_requests,
Xml::Namespace::kRequest,
Xml::Attribute::kProtocol,
Xml::Value::kProtocol);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[AddXMLAttributeNode failed][0x%x]"), hr));
return hr;
}
hr = AddXMLAttributeNode(update_requests,
Xml::Namespace::kRequest,
Xml::Attribute::kVersion,
req.version());
if (FAILED(hr)) { return hr; }
hr = AddXMLAttributeNode(update_requests,
Xml::Namespace::kRequest,
Xml::Attribute::kIsMachine,
req.is_machine() ? _T("1") : _T("0"));
if (FAILED(hr)) { return hr; }
if (!req.test_source().IsEmpty()) {
hr = AddXMLAttributeNode(update_requests,
Xml::Namespace::kRequest,
Xml::Attribute::kTestSource,
req.test_source());
if (FAILED(hr)) { return hr; }
}
if (!req.request_id().IsEmpty()) {
hr = AddXMLAttributeNode(update_requests,
Xml::Namespace::kRequest,
Xml::Attribute::kRequestId,
req.request_id());
if (FAILED(hr)) { return hr; }
}
bool is_period_overridden = false;
const int check_period_sec =
ConfigManager::Instance()->GetLastCheckPeriodSec(&is_period_overridden);
if (is_period_overridden) {
hr = AddXMLAttributeNode(update_requests,
Xml::Namespace::kRequest,
Xml::Attribute::kPeriodOverrideSec,
itostr(check_period_sec));
ASSERT1(SUCCEEDED(hr));
}
// Create os elements and add to the requests element.
CComPtr<IXMLDOMNode> os_element;
hr = CreateRequestElement(Xml::Element::kOs, _T(""), document, &os_element);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[CreateRequestElement failed][0x%x]"), hr));
return hr;
}
hr = AddXMLAttributeNode(os_element,
Xml::Namespace::kRequest,
Xml::Attribute::kPlatform,
kPlatformWin);
hr = AddXMLAttributeNode(os_element,
Xml::Namespace::kRequest,
Xml::Attribute::kVersion,
req.os_version());
hr = AddXMLAttributeNode(os_element,
Xml::Namespace::kRequest,
Xml::Attribute::kServicePack,
req.os_service_pack());
if (FAILED(hr)) { return hr; }
hr = update_requests->appendChild(os_element, NULL);
if (FAILED(hr)) { return hr; }
// Create and add request's to the requests node.
AppRequestVector::const_iterator i;
for (i = req.app_requests_begin(); i != req.app_requests_end(); ++i) {
const AppRequest& app_request = *i;
// Create each of the request elements and add to the requests element.
CComPtr<IXMLDOMNode> request_element;
hr = is_update_check ?
CreateUpdateAppRequestElement(app_request,
document,
&request_element) :
CreatePingAppRequestElement(app_request,
document,
&request_element);
if (FAILED(hr)) { return hr; }
// Add the update request node to the AppRequests node.
hr = update_requests->appendChild(request_element, NULL);
if (FAILED(hr)) { return hr; }
}
// Extract the string out of the DOM, and add the initial processing
// instruction. We cannot use the xml processing instruction as the get_xml
// method returns utf-16 encoded string which causes the utf-8 marker to be
// removed. We will convert to utf-8 before the actual save.
CComBSTR xml_value(Xml::kHeaderText);
CComBSTR xml_body;
document->get_xml(&xml_body);
xml_value += xml_body;
*request_buffer = static_cast<TCHAR*>(xml_value);
return S_OK;
}
HRESULT GoopdateXmlParser::LoadXmlFileToMemory(const CString& file_name,
std::vector<byte>* buffer) {
ASSERT1(buffer);
CComPtr<IXMLDOMDocument> document;
HRESULT hr = LoadXMLFromFile(file_name, false, &document);
if (FAILED(hr)) {
return hr;
}
return SaveXMLToRawData(document, buffer);
}
} // namespace omaha