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.
5 #include "chrome/browser/profile_resetter/brandcode_config_fetcher.h"
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"
18 const int kDownloadTimeoutSec
= 10;
19 const 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"
25 " appid=\"{8A69D345-D564-463C-AFF1-A69D9E530F96}\"\n"
26 " version=\"0.0.0.0\"\n"
29 " <data name=\"install\" "
30 "index=\"__BRANDCODE_PLACEHOLDER__\" />\n"
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.
36 std::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
);
46 // Extracts json master prefs from xml.
47 class XmlConfigParser
{
52 // Returns the content of /response/app/data tag.
53 static void Parse(const std::string
& input_buffer
,
54 std::string
* output_buffer
);
57 static XmlConfigParser
* FromContext(void* ctx
);
58 static std::string
XMLCharToString(const xmlChar
* value
);
59 static void StartElementImpl(void* ctx
,
61 const xmlChar
** atts
);
62 static void EndElementImpl(void* ctx
, const xmlChar
* name
);
63 static void CharactersImpl(void* ctx
, const xmlChar
* ch
, int len
);
65 bool IsParsingData() const;
67 // Extracted json file.
68 std::string master_prefs_
;
70 // Current stack of the elements being parsed.
71 std::vector
<std::string
> elements_
;
73 DISALLOW_COPY_AND_ASSIGN(XmlConfigParser
);
76 XmlConfigParser::XmlConfigParser() {}
78 XmlConfigParser::~XmlConfigParser() {}
80 void XmlConfigParser::Parse(const std::string
& input_buffer
,
81 std::string
* output_buffer
) {
82 using logging::LOG_WARNING
;
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
,
95 VLOG(LOG_WARNING
) << "Error parsing brandcoded master prefs, err=" << error
;
97 output_buffer
->swap(parser
.master_prefs_
);
101 XmlConfigParser
* XmlConfigParser::FromContext(void* ctx
) {
102 return static_cast<XmlConfigParser
*>(ctx
);
105 std::string
XmlConfigParser::XMLCharToString(const xmlChar
* value
) {
106 return std::string(reinterpret_cast<const char*>(value
));
109 void XmlConfigParser::StartElementImpl(void* ctx
,
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();
119 void XmlConfigParser::EndElementImpl(void* ctx
, const xmlChar
* name
) {
120 XmlConfigParser
* context
= FromContext(ctx
);
121 context
->elements_
.pop_back();
124 void 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
);
132 bool 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
);
140 BrandcodeConfigFetcher::BrandcodeConfigFetcher(const FetchCallback
& callback
,
142 const std::string
& brandcode
)
143 : fetch_callback_(callback
) {
144 DCHECK(!brandcode
.empty());
145 config_fetcher_
= net::URLFetcher::Create(0 /* ID used for testing */, url
,
146 net::URLFetcher::POST
, this);
147 config_fetcher_
->SetRequestContext(
148 g_browser_process
->system_request_context());
149 config_fetcher_
->SetUploadData("text/xml", GetUploadData(brandcode
));
150 config_fetcher_
->AddExtraRequestHeader("Accept: text/xml");
151 config_fetcher_
->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES
|
152 net::LOAD_DO_NOT_SAVE_COOKIES
|
153 net::LOAD_DISABLE_CACHE
);
154 config_fetcher_
->Start();
155 // Abort the download attempt if it takes too long.
156 download_timer_
.Start(FROM_HERE
,
157 base::TimeDelta::FromSeconds(kDownloadTimeoutSec
),
159 &BrandcodeConfigFetcher::OnDownloadTimeout
);
162 BrandcodeConfigFetcher::~BrandcodeConfigFetcher() {}
164 void BrandcodeConfigFetcher::SetCallback(const FetchCallback
& callback
) {
165 fetch_callback_
= callback
;
168 void BrandcodeConfigFetcher::OnURLFetchComplete(const net::URLFetcher
* source
) {
169 if (source
!= config_fetcher_
.get()) {
170 NOTREACHED() << "Callback from foreign URL fetcher";
173 std::string response_string
;
174 std::string mime_type
;
175 if (config_fetcher_
&&
176 config_fetcher_
->GetStatus().is_success() &&
177 config_fetcher_
->GetResponseCode() == 200 &&
178 config_fetcher_
->GetResponseHeaders()->GetMimeType(&mime_type
) &&
179 mime_type
== "text/xml" &&
180 config_fetcher_
->GetResponseAsString(&response_string
)) {
181 std::string master_prefs
;
182 XmlConfigParser::Parse(response_string
, &master_prefs
);
183 default_settings_
.reset(new BrandcodedDefaultSettings(master_prefs
));
185 config_fetcher_
.reset();
186 download_timer_
.Stop();
187 fetch_callback_
.Run();
190 void BrandcodeConfigFetcher::OnDownloadTimeout() {
191 if (config_fetcher_
) {
192 config_fetcher_
.reset();
193 fetch_callback_
.Run();