Home | History | Annotate | Download | only in profile_resetter
      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 
     16 namespace {
     17 
     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"
     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.
     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);
     43   return data;
     44 }
     45 
     46 // Extracts json master prefs from xml.
     47 class 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 
     76 XmlConfigParser::XmlConfigParser() {}
     77 
     78 XmlConfigParser::~XmlConfigParser() {}
     79 
     80 void 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 
    101 XmlConfigParser* XmlConfigParser::FromContext(void* ctx) {
    102   return static_cast<XmlConfigParser*>(ctx);
    103 }
    104 
    105 std::string XmlConfigParser::XMLCharToString(const xmlChar* value) {
    106   return std::string(reinterpret_cast<const char*>(value));
    107 }
    108 
    109 void 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 
    119 void XmlConfigParser::EndElementImpl(void* ctx, const xmlChar* name) {
    120   XmlConfigParser* context = FromContext(ctx);
    121   context->elements_.pop_back();
    122 }
    123 
    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);
    129   }
    130 }
    131 
    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);
    136 }
    137 
    138 } // namespace
    139 
    140 BrandcodeConfigFetcher::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 
    164 BrandcodeConfigFetcher::~BrandcodeConfigFetcher() {}
    165 
    166 void BrandcodeConfigFetcher::SetCallback(const FetchCallback& callback) {
    167   fetch_callback_ = callback;
    168 }
    169 
    170 void 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 
    192 void BrandcodeConfigFetcher::OnDownloadTimeout() {
    193   if (config_fetcher_) {
    194     config_fetcher_.reset();
    195     fetch_callback_.Run();
    196   }
    197 }
    198