1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/profile_resetter/brandcode_config_fetcher.h" 6 7#include "base/strings/utf_string_conversions.h" 8#include "chrome/browser/browser_process.h" 9#include "chrome/browser/profile_resetter/brandcoded_default_settings.h" 10#include "libxml/parser.h" 11#include "net/base/load_flags.h" 12#include "net/http/http_response_headers.h" 13#include "net/url_request/url_fetcher.h" 14#include "net/url_request/url_request_status.h" 15 16namespace { 17 18const int kDownloadTimeoutSec = 10; 19const char kPostXml[] = 20"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 21"<request version=\"1.3.17.0\" protocol=\"3.0\" testsource=\"dev\" " 22 "shell_version=\"1.2.3.5\">\n" 23" <os platform=\"win\" version=\"6.1\" sp=\"\" arch=\"x86\" />\n" 24" <app\n" 25" appid=\"{8A69D345-D564-463C-AFF1-A69D9E530F96}\"\n" 26" version=\"0.0.0.0\"\n" 27" >\n" 28" <updatecheck />\n" 29" <data name=\"install\" " 30 "index=\"__BRANDCODE_PLACEHOLDER__\" />\n" 31" </app>\n" 32"</request>"; 33 34// Returns the query to the server which can be used to retrieve the config. 35// |brand| is a brand code, it mustn't be empty. 36std::string GetUploadData(const std::string& brand) { 37 DCHECK(!brand.empty()); 38 std::string data(kPostXml); 39 const std::string placeholder("__BRANDCODE_PLACEHOLDER__"); 40 size_t placeholder_pos = data.find(placeholder); 41 DCHECK(placeholder_pos != std::string::npos); 42 data.replace(placeholder_pos, placeholder.size(), brand); 43 return data; 44} 45 46// Extracts json master prefs from xml. 47class XmlConfigParser { 48 public: 49 XmlConfigParser(); 50 ~XmlConfigParser(); 51 52 // Returns the content of /response/app/data tag. 53 static void Parse(const std::string& input_buffer, 54 std::string* output_buffer); 55 56 private: 57 static XmlConfigParser* FromContext(void* ctx); 58 static std::string XMLCharToString(const xmlChar* value); 59 static void StartElementImpl(void* ctx, 60 const xmlChar* name, 61 const xmlChar** atts); 62 static void EndElementImpl(void* ctx, const xmlChar* name); 63 static void CharactersImpl(void* ctx, const xmlChar* ch, int len); 64 65 bool IsParsingData() const; 66 67 // Extracted json file. 68 std::string master_prefs_; 69 70 // Current stack of the elements being parsed. 71 std::vector<std::string> elements_; 72 73 DISALLOW_COPY_AND_ASSIGN(XmlConfigParser); 74}; 75 76XmlConfigParser::XmlConfigParser() {} 77 78XmlConfigParser::~XmlConfigParser() {} 79 80void XmlConfigParser::Parse(const std::string& input_buffer, 81 std::string* output_buffer) { 82 using logging::LOG_WARNING; 83 84 DCHECK(output_buffer); 85 xmlSAXHandler sax_handler = {}; 86 sax_handler.startElement = &XmlConfigParser::StartElementImpl; 87 sax_handler.endElement = &XmlConfigParser::EndElementImpl; 88 sax_handler.characters = &XmlConfigParser::CharactersImpl; 89 XmlConfigParser parser; 90 int error = xmlSAXUserParseMemory(&sax_handler, 91 &parser, 92 input_buffer.c_str(), 93 input_buffer.size()); 94 if (error) { 95 VLOG(LOG_WARNING) << "Error parsing brandcoded master prefs, err=" << error; 96 } else { 97 output_buffer->swap(parser.master_prefs_); 98 } 99} 100 101XmlConfigParser* XmlConfigParser::FromContext(void* ctx) { 102 return static_cast<XmlConfigParser*>(ctx); 103} 104 105std::string XmlConfigParser::XMLCharToString(const xmlChar* value) { 106 return std::string(reinterpret_cast<const char*>(value)); 107} 108 109void XmlConfigParser::StartElementImpl(void* ctx, 110 const xmlChar* name, 111 const xmlChar** atts) { 112 std::string node_name(XMLCharToString(name)); 113 XmlConfigParser* context = FromContext(ctx); 114 context->elements_.push_back(node_name); 115 if (context->IsParsingData()) 116 context->master_prefs_.clear(); 117} 118 119void XmlConfigParser::EndElementImpl(void* ctx, const xmlChar* name) { 120 XmlConfigParser* context = FromContext(ctx); 121 context->elements_.pop_back(); 122} 123 124void XmlConfigParser::CharactersImpl(void* ctx, const xmlChar* ch, int len) { 125 XmlConfigParser* context = FromContext(ctx); 126 if (context->IsParsingData()) { 127 context->master_prefs_ += 128 std::string(reinterpret_cast<const char*>(ch), len); 129 } 130} 131 132bool XmlConfigParser::IsParsingData() const { 133 const std::string data_path[] = {"response", "app", "data"}; 134 return elements_.size() == arraysize(data_path) && 135 std::equal(elements_.begin(), elements_.end(), data_path); 136} 137 138} // namespace 139 140BrandcodeConfigFetcher::BrandcodeConfigFetcher(const FetchCallback& callback, 141 const GURL& url, 142 const std::string& brandcode) 143 : fetch_callback_(callback) { 144 DCHECK(!brandcode.empty()); 145 config_fetcher_.reset(net::URLFetcher::Create(0 /* ID used for testing */, 146 url, 147 net::URLFetcher::POST, 148 this)); 149 config_fetcher_->SetRequestContext( 150 g_browser_process->system_request_context()); 151 config_fetcher_->SetUploadData("text/xml", GetUploadData(brandcode)); 152 config_fetcher_->AddExtraRequestHeader("Accept: text/xml"); 153 config_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | 154 net::LOAD_DO_NOT_SAVE_COOKIES | 155 net::LOAD_DISABLE_CACHE); 156 config_fetcher_->Start(); 157 // Abort the download attempt if it takes too long. 158 download_timer_.Start(FROM_HERE, 159 base::TimeDelta::FromSeconds(kDownloadTimeoutSec), 160 this, 161 &BrandcodeConfigFetcher::OnDownloadTimeout); 162} 163 164BrandcodeConfigFetcher::~BrandcodeConfigFetcher() {} 165 166void BrandcodeConfigFetcher::SetCallback(const FetchCallback& callback) { 167 fetch_callback_ = callback; 168} 169 170void BrandcodeConfigFetcher::OnURLFetchComplete(const net::URLFetcher* source) { 171 if (source != config_fetcher_.get()) { 172 NOTREACHED() << "Callback from foreign URL fetcher"; 173 return; 174 } 175 std::string response_string; 176 std::string mime_type; 177 if (config_fetcher_ && 178 config_fetcher_->GetStatus().is_success() && 179 config_fetcher_->GetResponseCode() == 200 && 180 config_fetcher_->GetResponseHeaders()->GetMimeType(&mime_type) && 181 mime_type == "text/xml" && 182 config_fetcher_->GetResponseAsString(&response_string)) { 183 std::string master_prefs; 184 XmlConfigParser::Parse(response_string, &master_prefs); 185 default_settings_.reset(new BrandcodedDefaultSettings(master_prefs)); 186 } 187 config_fetcher_.reset(); 188 download_timer_.Stop(); 189 fetch_callback_.Run(); 190} 191 192void BrandcodeConfigFetcher::OnDownloadTimeout() { 193 if (config_fetcher_) { 194 config_fetcher_.reset(); 195 fetch_callback_.Run(); 196 } 197} 198