1 // Copyright 2014 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 "extensions/browser/updater/manifest_fetch_data.h" 6 7 #include <vector> 8 9 #include "base/logging.h" 10 #include "base/metrics/histogram.h" 11 #include "base/strings/string_number_conversions.h" 12 #include "base/strings/string_util.h" 13 #include "base/strings/stringprintf.h" 14 #include "net/base/escape.h" 15 16 namespace { 17 18 // Maximum length of an extension manifest update check url, since it is a GET 19 // request. We want to stay under 2K because of proxies, etc. 20 const int kExtensionsManifestMaxURLSize = 2000; 21 22 } // namespace 23 24 namespace extensions { 25 26 ManifestFetchData::ManifestFetchData(const GURL& update_url, 27 int request_id, 28 const std::string& brand_code, 29 const std::string& base_query_params, 30 PingMode ping_mode) 31 : base_url_(update_url), 32 full_url_(update_url), 33 brand_code_(brand_code), 34 ping_mode_(ping_mode) { 35 std::string query = 36 full_url_.has_query() ? full_url_.query() + "&" : std::string(); 37 query += base_query_params; 38 GURL::Replacements replacements; 39 replacements.SetQueryStr(query); 40 full_url_ = full_url_.ReplaceComponents(replacements); 41 42 request_ids_.insert(request_id); 43 } 44 45 ManifestFetchData::~ManifestFetchData() { 46 } 47 48 // The format for request parameters in update checks is: 49 // 50 // ?x=EXT1_INFO&x=EXT2_INFO 51 // 52 // where EXT1_INFO and EXT2_INFO are url-encoded strings of the form: 53 // 54 // id=EXTENSION_ID&v=VERSION&uc 55 // 56 // Provide ping data with the parameter ping=PING_DATA where PING_DATA 57 // looks like r=DAYS or a=DAYS for extensions in the Chrome extensions gallery. 58 // ('r' refers to 'roll call' ie installation, and 'a' refers to 'active'). 59 // These values will each be present at most once every 24 hours, and indicate 60 // the number of days since the last time it was present in an update check. 61 // 62 // So for two extensions like: 63 // Extension 1- id:aaaa version:1.1 64 // Extension 2- id:bbbb version:2.0 65 // 66 // the full update url would be: 67 // http://somehost/path?x=id%3Daaaa%26v%3D1.1%26uc&x=id%3Dbbbb%26v%3D2.0%26uc 68 // 69 // (Note that '=' is %3D and '&' is %26 when urlencoded.) 70 bool ManifestFetchData::AddExtension(const std::string& id, 71 const std::string& version, 72 const PingData* ping_data, 73 const std::string& update_url_data, 74 const std::string& install_source, 75 bool force_update) { 76 if (extension_ids_.find(id) != extension_ids_.end()) { 77 NOTREACHED() << "Duplicate extension id " << id; 78 return false; 79 } 80 81 if (force_update) 82 forced_updates_.insert(id); 83 84 // If we want to force an update, we send 0.0.0.0 as the installed version 85 // number. 86 const std::string installed_version = force_update ? "0.0.0.0" : version; 87 88 // Compute the string we'd append onto the full_url_, and see if it fits. 89 std::vector<std::string> parts; 90 parts.push_back("id=" + id); 91 parts.push_back("v=" + installed_version); 92 if (!install_source.empty()) 93 parts.push_back("installsource=" + install_source); 94 parts.push_back("uc"); 95 96 if (!update_url_data.empty()) { 97 // Make sure the update_url_data string is escaped before using it so that 98 // there is no chance of overriding the id or v other parameter value 99 // we place into the x= value. 100 parts.push_back("ap=" + net::EscapeQueryParamValue(update_url_data, true)); 101 } 102 103 // Append brand code, rollcall and active ping parameters. 104 if (ping_mode_ != NO_PING) { 105 if (!brand_code_.empty()) 106 parts.push_back(base::StringPrintf("brand=%s", brand_code_.c_str())); 107 108 std::string ping_value; 109 pings_[id] = PingData(0, 0, false); 110 if (ping_data) { 111 if (ping_data->rollcall_days == kNeverPinged || 112 ping_data->rollcall_days > 0) { 113 ping_value += "r=" + base::IntToString(ping_data->rollcall_days); 114 if (ping_mode_ == PING_WITH_METRICS) { 115 ping_value += "&e=" + std::string(ping_data->is_enabled ? "1" : "0"); 116 } 117 pings_[id].rollcall_days = ping_data->rollcall_days; 118 pings_[id].is_enabled = ping_data->is_enabled; 119 } 120 if (ping_data->active_days == kNeverPinged || 121 ping_data->active_days > 0) { 122 if (!ping_value.empty()) 123 ping_value += "&"; 124 ping_value += "a=" + base::IntToString(ping_data->active_days); 125 pings_[id].active_days = ping_data->active_days; 126 } 127 } 128 if (!ping_value.empty()) 129 parts.push_back("ping=" + net::EscapeQueryParamValue(ping_value, true)); 130 } 131 132 std::string extra = full_url_.has_query() ? "&" : "?"; 133 extra += "x=" + net::EscapeQueryParamValue(JoinString(parts, '&'), true); 134 135 // Check against our max url size, exempting the first extension added. 136 int new_size = full_url_.possibly_invalid_spec().size() + extra.size(); 137 if (!extension_ids_.empty() && new_size > kExtensionsManifestMaxURLSize) { 138 UMA_HISTOGRAM_PERCENTAGE("Extensions.UpdateCheckHitUrlSizeLimit", 1); 139 return false; 140 } 141 UMA_HISTOGRAM_PERCENTAGE("Extensions.UpdateCheckHitUrlSizeLimit", 0); 142 143 // We have room so go ahead and add the extension. 144 extension_ids_.insert(id); 145 full_url_ = GURL(full_url_.possibly_invalid_spec() + extra); 146 return true; 147 } 148 149 bool ManifestFetchData::Includes(const std::string& extension_id) const { 150 return extension_ids_.find(extension_id) != extension_ids_.end(); 151 } 152 153 bool ManifestFetchData::DidPing(const std::string& extension_id, 154 PingType type) const { 155 std::map<std::string, PingData>::const_iterator i = pings_.find(extension_id); 156 if (i == pings_.end()) 157 return false; 158 int value = 0; 159 if (type == ROLLCALL) 160 value = i->second.rollcall_days; 161 else if (type == ACTIVE) 162 value = i->second.active_days; 163 else 164 NOTREACHED(); 165 return value == kNeverPinged || value > 0; 166 } 167 168 void ManifestFetchData::Merge(const ManifestFetchData& other) { 169 DCHECK(full_url() == other.full_url()); 170 request_ids_.insert(other.request_ids_.begin(), other.request_ids_.end()); 171 } 172 173 bool ManifestFetchData::DidForceUpdate(const std::string& extension_id) const { 174 return forced_updates_.find(extension_id) != forced_updates_.end(); 175 } 176 177 } // namespace extensions 178