extension_updater.cc revision c407dc5cd9bdc5668497f21b26b09d988ab439de
1// Copyright (c) 2010 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/extensions/extension_updater.h"
6
7#include <algorithm>
8#include <set>
9
10#include "base/logging.h"
11#include "base/file_util.h"
12#include "base/file_version_info.h"
13#include "base/histogram.h"
14#include "base/rand_util.h"
15#include "base/sha2.h"
16#include "base/stl_util-inl.h"
17#include "base/string_util.h"
18#include "base/time.h"
19#include "base/thread.h"
20#include "base/version.h"
21#include "chrome/browser/browser_process.h"
22#include "chrome/browser/extensions/extension_error_reporter.h"
23#include "chrome/browser/extensions/extensions_service.h"
24#include "chrome/browser/pref_service.h"
25#include "chrome/browser/profile.h"
26#include "chrome/browser/utility_process_host.h"
27#include "chrome/common/chrome_switches.h"
28#include "chrome/common/chrome_version_info.h"
29#include "chrome/common/extensions/extension.h"
30#include "chrome/common/extensions/extension_constants.h"
31#include "chrome/common/pref_names.h"
32#include "googleurl/src/gurl.h"
33#include "net/base/escape.h"
34#include "net/base/load_flags.h"
35#include "net/url_request/url_request_status.h"
36
37#if defined(OS_WIN)
38#include "base/registry.h"
39#elif defined(OS_MACOSX)
40#include "base/sys_string_conversions.h"
41#endif
42
43using base::RandDouble;
44using base::RandInt;
45using base::Time;
46using base::TimeDelta;
47using prefs::kExtensionBlacklistUpdateVersion;
48using prefs::kLastExtensionsUpdateCheck;
49using prefs::kNextExtensionsUpdateCheck;
50
51// NOTE: HTTPS is used here to ensure the response from omaha can be trusted.
52// The response contains a url for fetching the blacklist and a hash value
53// for validation.
54const char* ExtensionUpdater::kBlacklistUpdateUrl =
55    "https://clients2.google.com/service/update2/crx";
56
57// Update AppID for extension blacklist.
58const char* ExtensionUpdater::kBlacklistAppID = "com.google.crx.blacklist";
59
60// Wait at least 5 minutes after browser startup before we do any checks. If you
61// change this value, make sure to update comments where it is used.
62const int kStartupWaitSeconds = 60 * 5;
63
64// For sanity checking on update frequency - enforced in release mode only.
65static const int kMinUpdateFrequencySeconds = 30;
66static const int kMaxUpdateFrequencySeconds = 60 * 60 * 24 * 7;  // 7 days
67
68// Maximum length of an extension manifest update check url, since it is a GET
69// request. We want to stay under 2K because of proxies, etc.
70static const int kExtensionsManifestMaxURLSize = 2000;
71
72
73// The format for request parameters in update checks is:
74//
75//   ?x=EXT1_INFO&x=EXT2_INFO
76//
77// where EXT1_INFO and EXT2_INFO are url-encoded strings of the form:
78//
79//   id=EXTENSION_ID&v=VERSION&uc
80//
81// Additionally, we may include the parameter ping=PING_DATA where PING_DATA
82// looks like r=DAYS for extensions in the Chrome extensions gallery. This value
83// will be present at most once every 24 hours, and indicate the number of days
84// since the last time it was present in an update check.
85//
86// So for two extensions like:
87//   Extension 1- id:aaaa version:1.1
88//   Extension 2- id:bbbb version:2.0
89//
90// the full update url would be:
91//   http://somehost/path?x=id%3Daaaa%26v%3D1.1%26uc&x=id%3Dbbbb%26v%3D2.0%26uc
92//
93// (Note that '=' is %3D and '&' is %26 when urlencoded.)
94bool ManifestFetchData::AddExtension(std::string id, std::string version,
95                                     int days) {
96  if (extension_ids_.find(id) != extension_ids_.end()) {
97    NOTREACHED() << "Duplicate extension id " << id;
98    return false;
99  }
100
101  // Compute the string we'd append onto the full_url_, and see if it fits.
102  std::vector<std::string> parts;
103  parts.push_back("id=" + id);
104  parts.push_back("v=" + version);
105  parts.push_back("uc");
106
107  if (ShouldPing(days)) {
108    parts.push_back("ping=" + EscapeQueryParamValue("r=" + IntToString(days),
109                                                    true));
110  }
111
112  std::string extra = full_url_.has_query() ? "&" : "?";
113  extra += "x=" + EscapeQueryParamValue(JoinString(parts, '&'), true);
114
115  // Check against our max url size, exempting the first extension added.
116  int new_size = full_url_.possibly_invalid_spec().size() + extra.size();
117  if (extension_ids_.size() > 0 && new_size > kExtensionsManifestMaxURLSize) {
118    UMA_HISTOGRAM_PERCENTAGE("Extensions.UpdateCheckHitUrlSizeLimit", 1);
119    return false;
120  }
121  UMA_HISTOGRAM_PERCENTAGE("Extensions.UpdateCheckHitUrlSizeLimit", 0);
122
123  // We have room so go ahead and add the extension.
124  extension_ids_.insert(id);
125  ping_days_[id] = days;
126  full_url_ = GURL(full_url_.possibly_invalid_spec() + extra);
127  return true;
128}
129
130bool ManifestFetchData::DidPing(std::string extension_id) const {
131  std::map<std::string, int>::const_iterator i = ping_days_.find(extension_id);
132  if (i != ping_days_.end()) {
133    return ShouldPing(i->second);
134  }
135  return false;
136}
137
138bool ManifestFetchData::ShouldPing(int days) const {
139  return base_url_.DomainIs("google.com") &&
140         (days == kNeverPinged || days > 0);
141}
142
143namespace {
144
145// Calculates the value to use for the ping days parameter.
146static int CalculatePingDays(const Time& last_ping_day) {
147  int days = ManifestFetchData::kNeverPinged;
148  if (!last_ping_day.is_null()) {
149    days = (Time::Now() - last_ping_day).InDays();
150  }
151  return days;
152}
153
154}  // namespace
155
156ManifestFetchesBuilder::ManifestFetchesBuilder(
157    ExtensionUpdateService* service) : service_(service) {
158  DCHECK(service_);
159}
160
161void ManifestFetchesBuilder::AddExtension(const Extension& extension) {
162  AddExtensionData(extension.location(),
163                   extension.id(),
164                   *extension.version(),
165                   extension.is_theme(),
166                   extension.update_url());
167}
168
169void ManifestFetchesBuilder::AddPendingExtension(
170    const std::string& id,
171    const PendingExtensionInfo& info) {
172  // Use a zero version to ensure that a pending extension will always
173  // be updated, and thus installed (assuming all extensions have
174  // non-zero versions).
175  scoped_ptr<Version> version(
176      Version::GetVersionFromString("0.0.0.0"));
177  AddExtensionData(Extension::INTERNAL, id, *version,
178                   info.is_theme, info.update_url);
179}
180
181void ManifestFetchesBuilder::ReportStats() const {
182  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckExtensions",
183                           url_stats_.google_url_count +
184                           url_stats_.other_url_count -
185                           url_stats_.theme_count);
186  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckTheme",
187                           url_stats_.theme_count);
188  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckGoogleUrl",
189                           url_stats_.google_url_count);
190  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckOtherUrl",
191                           url_stats_.other_url_count);
192  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckNoUrl",
193                           url_stats_.no_url_count);
194}
195
196std::vector<ManifestFetchData*> ManifestFetchesBuilder::GetFetches() {
197  std::vector<ManifestFetchData*> fetches;
198  fetches.reserve(fetches_.size());
199  for (std::multimap<GURL, ManifestFetchData*>::iterator it =
200           fetches_.begin(); it != fetches_.end(); ++it) {
201    fetches.push_back(it->second);
202  }
203  fetches_.clear();
204  url_stats_ = URLStats();
205  return fetches;
206}
207
208void ManifestFetchesBuilder::AddExtensionData(
209    Extension::Location location,
210    const std::string& id,
211    const Version& version,
212    bool is_theme,
213    GURL update_url) {
214  // Only internal and external extensions can be autoupdated.
215  if (location != Extension::INTERNAL &&
216      !Extension::IsExternalLocation(location)) {
217    return;
218  }
219
220  // Skip extensions with non-empty invalid update URLs.
221  if (!update_url.is_empty() && !update_url.is_valid()) {
222    LOG(WARNING) << "Extension " << id << " has invalid update url "
223                 << update_url;
224    return;
225  }
226
227  // Skip extensions with empty IDs.
228  if (id.empty()) {
229    LOG(WARNING) << "Found extension with empty ID";
230    return;
231  }
232
233  if (update_url.DomainIs("google.com")) {
234    url_stats_.google_url_count++;
235  } else if (update_url.is_empty()) {
236    url_stats_.no_url_count++;
237    // Fill in default update URL.
238    //
239    // TODO(akalin): Figure out if we should use the HTTPS version.
240    update_url = GURL(extension_urls::kGalleryUpdateHttpUrl);
241  } else {
242    url_stats_.other_url_count++;
243  }
244
245  if (is_theme) {
246    url_stats_.theme_count++;
247  }
248
249  DCHECK(!update_url.is_empty());
250  DCHECK(update_url.is_valid());
251
252  ManifestFetchData* fetch = NULL;
253  std::multimap<GURL, ManifestFetchData*>::iterator existing_iter =
254      fetches_.find(update_url);
255
256  // Find or create a ManifestFetchData to add this extension to.
257  int ping_days =
258      CalculatePingDays(service_->extension_prefs()->LastPingDay(id));
259  while (existing_iter != fetches_.end()) {
260    if (existing_iter->second->AddExtension(id, version.GetString(),
261                                            ping_days)) {
262      fetch = existing_iter->second;
263      break;
264    }
265    existing_iter++;
266  }
267  if (!fetch) {
268    fetch = new ManifestFetchData(update_url);
269    fetches_.insert(std::pair<GURL, ManifestFetchData*>(update_url, fetch));
270    bool added = fetch->AddExtension(id, version.GetString(), ping_days);
271    DCHECK(added);
272  }
273}
274
275// A utility class to do file handling on the file I/O thread.
276class ExtensionUpdaterFileHandler
277    : public base::RefCountedThreadSafe<ExtensionUpdaterFileHandler> {
278 public:
279  // Writes crx file data into a tempfile, and calls back the updater.
280  void WriteTempFile(const std::string& extension_id, const std::string& data,
281                     const GURL& download_url,
282                     scoped_refptr<ExtensionUpdater> updater) {
283    // Make sure we're running in the right thread.
284    DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
285
286    FilePath path;
287    if (!file_util::CreateTemporaryFile(&path)) {
288      LOG(WARNING) << "Failed to create temporary file path";
289      return;
290    }
291    if (file_util::WriteFile(path, data.c_str(), data.length()) !=
292        static_cast<int>(data.length())) {
293      // TODO(asargent) - It would be nice to back off updating alltogether if
294      // the disk is full. (http://crbug.com/12763).
295      LOG(ERROR) << "Failed to write temporary file";
296      file_util::Delete(path, false);
297      return;
298    }
299
300    // The ExtensionUpdater now owns the temp file.
301    ChromeThread::PostTask(
302        ChromeThread::UI, FROM_HERE,
303        NewRunnableMethod(
304            updater.get(), &ExtensionUpdater::OnCRXFileWritten, extension_id,
305            path, download_url));
306  }
307
308 private:
309  friend class base::RefCountedThreadSafe<ExtensionUpdaterFileHandler>;
310
311  ~ExtensionUpdaterFileHandler() {}
312};
313
314ExtensionUpdater::ExtensionUpdater(ExtensionUpdateService* service,
315                                   PrefService* prefs,
316                                   int frequency_seconds)
317    : service_(service), frequency_seconds_(frequency_seconds),
318      prefs_(prefs), file_handler_(new ExtensionUpdaterFileHandler()),
319      blacklist_checks_enabled_(true) {
320  Init();
321}
322
323void ExtensionUpdater::Init() {
324  DCHECK_GE(frequency_seconds_, 5);
325  DCHECK(frequency_seconds_ <= kMaxUpdateFrequencySeconds);
326#ifdef NDEBUG
327  // In Release mode we enforce that update checks don't happen too often.
328  frequency_seconds_ = std::max(frequency_seconds_, kMinUpdateFrequencySeconds);
329#endif
330  frequency_seconds_ = std::min(frequency_seconds_, kMaxUpdateFrequencySeconds);
331}
332
333ExtensionUpdater::~ExtensionUpdater() {
334  STLDeleteElements(&manifests_pending_);
335}
336
337static void EnsureInt64PrefRegistered(PrefService* prefs,
338                                      const wchar_t name[]) {
339  if (!prefs->FindPreference(name))
340    prefs->RegisterInt64Pref(name, 0);
341}
342
343static void EnsureBlacklistVersionPrefRegistered(PrefService* prefs) {
344  if (!prefs->FindPreference(kExtensionBlacklistUpdateVersion))
345    prefs->RegisterStringPref(kExtensionBlacklistUpdateVersion, "0");
346}
347
348// The overall goal here is to balance keeping clients up to date while
349// avoiding a thundering herd against update servers.
350TimeDelta ExtensionUpdater::DetermineFirstCheckDelay() {
351  // If someone's testing with a quick frequency, just allow it.
352  if (frequency_seconds_ < kStartupWaitSeconds)
353    return TimeDelta::FromSeconds(frequency_seconds_);
354
355  // If we've never scheduled a check before, start at frequency_seconds_.
356  if (!prefs_->HasPrefPath(kNextExtensionsUpdateCheck))
357    return TimeDelta::FromSeconds(frequency_seconds_);
358
359  // If it's been a long time since our last actual check, we want to do one
360  // relatively soon.
361  Time now = Time::Now();
362  Time last = Time::FromInternalValue(prefs_->GetInt64(
363      kLastExtensionsUpdateCheck));
364  int days = (now - last).InDays();
365  if (days >= 30) {
366    // Wait 5-10 minutes.
367    return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds,
368                                          kStartupWaitSeconds * 2));
369  } else if (days >= 14) {
370    // Wait 10-20 minutes.
371    return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds * 2,
372                                          kStartupWaitSeconds * 4));
373  } else if (days >= 3) {
374    // Wait 20-40 minutes.
375    return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds * 4,
376                                          kStartupWaitSeconds * 8));
377  }
378
379  // Read the persisted next check time, and use that if it isn't too soon.
380  // Otherwise pick something random.
381  Time saved_next = Time::FromInternalValue(prefs_->GetInt64(
382      kNextExtensionsUpdateCheck));
383  Time earliest = now + TimeDelta::FromSeconds(kStartupWaitSeconds);
384  if (saved_next >= earliest) {
385    return saved_next - now;
386  } else {
387    return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds,
388                                          frequency_seconds_));
389  }
390}
391
392void ExtensionUpdater::Start() {
393  // Make sure our prefs are registered, then schedule the first check.
394  EnsureInt64PrefRegistered(prefs_, kLastExtensionsUpdateCheck);
395  EnsureInt64PrefRegistered(prefs_, kNextExtensionsUpdateCheck);
396  EnsureBlacklistVersionPrefRegistered(prefs_);
397  ScheduleNextCheck(DetermineFirstCheckDelay());
398}
399
400void ExtensionUpdater::Stop() {
401  timer_.Stop();
402  manifest_fetcher_.reset();
403  extension_fetcher_.reset();
404  manifests_pending_.clear();
405  extensions_pending_.clear();
406}
407
408void ExtensionUpdater::OnURLFetchComplete(
409    const URLFetcher* source, const GURL& url, const URLRequestStatus& status,
410    int response_code, const ResponseCookies& cookies,
411    const std::string& data) {
412  if (source == manifest_fetcher_.get()) {
413    OnManifestFetchComplete(url, status, response_code, data);
414  } else if (source == extension_fetcher_.get()) {
415    OnCRXFetchComplete(url, status, response_code, data);
416  } else {
417    NOTREACHED();
418  }
419}
420
421// Utility class to handle doing xml parsing in a sandboxed utility process.
422class SafeManifestParser : public UtilityProcessHost::Client {
423 public:
424  // Takes ownership of |fetch_data|.
425  SafeManifestParser(const std::string& xml, ManifestFetchData* fetch_data,
426                     ExtensionUpdater* updater)
427      : xml_(xml), updater_(updater) {
428    fetch_data_.reset(fetch_data);
429  }
430
431  // Posts a task over to the IO loop to start the parsing of xml_ in a
432  // utility process.
433  void Start() {
434    DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
435    ChromeThread::PostTask(
436        ChromeThread::IO, FROM_HERE,
437        NewRunnableMethod(
438            this, &SafeManifestParser::ParseInSandbox,
439            g_browser_process->resource_dispatcher_host()));
440  }
441
442  // Creates the sandboxed utility process and tells it to start parsing.
443  void ParseInSandbox(ResourceDispatcherHost* rdh) {
444    DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
445
446    // TODO(asargent) we shouldn't need to do this branch here - instead
447    // UtilityProcessHost should handle it for us. (http://crbug.com/19192)
448    bool use_utility_process = rdh &&
449        !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess);
450    if (use_utility_process) {
451      UtilityProcessHost* host = new UtilityProcessHost(
452          rdh, this, ChromeThread::UI);
453      host->StartUpdateManifestParse(xml_);
454    } else {
455      UpdateManifest manifest;
456      if (manifest.Parse(xml_)) {
457        ChromeThread::PostTask(
458            ChromeThread::UI, FROM_HERE,
459            NewRunnableMethod(
460                this, &SafeManifestParser::OnParseUpdateManifestSucceeded,
461                manifest.results()));
462      } else {
463        ChromeThread::PostTask(
464            ChromeThread::UI, FROM_HERE,
465            NewRunnableMethod(
466                this, &SafeManifestParser::OnParseUpdateManifestFailed,
467                manifest.errors()));
468      }
469    }
470  }
471
472  // Callback from the utility process when parsing succeeded.
473  virtual void OnParseUpdateManifestSucceeded(
474      const UpdateManifest::Results& results) {
475    DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
476    updater_->HandleManifestResults(*fetch_data_, results);
477  }
478
479  // Callback from the utility process when parsing failed.
480  virtual void OnParseUpdateManifestFailed(const std::string& error_message) {
481    DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
482    LOG(WARNING) << "Error parsing update manifest:\n" << error_message;
483  }
484
485 private:
486  ~SafeManifestParser() {}
487
488  const std::string& xml_;
489  scoped_ptr<ManifestFetchData> fetch_data_;
490
491  scoped_refptr<ExtensionUpdater> updater_;
492};
493
494
495void ExtensionUpdater::OnManifestFetchComplete(const GURL& url,
496                                               const URLRequestStatus& status,
497                                               int response_code,
498                                               const std::string& data) {
499  // We want to try parsing the manifest, and if it indicates updates are
500  // available, we want to fire off requests to fetch those updates.
501  if (status.status() == URLRequestStatus::SUCCESS && response_code == 200) {
502    scoped_refptr<SafeManifestParser> safe_parser =
503        new SafeManifestParser(data, current_manifest_fetch_.release(), this);
504    safe_parser->Start();
505  } else {
506    // TODO(asargent) Do exponential backoff here. (http://crbug.com/12546).
507    LOG(INFO) << "Failed to fetch manifest '" << url.possibly_invalid_spec() <<
508        "' response code:" << response_code;
509  }
510  manifest_fetcher_.reset();
511  current_manifest_fetch_.reset();
512
513  // If we have any pending manifest requests, fire off the next one.
514  if (!manifests_pending_.empty()) {
515    ManifestFetchData* manifest_fetch = manifests_pending_.front();
516    manifests_pending_.pop_front();
517    StartUpdateCheck(manifest_fetch);
518  }
519}
520
521void ExtensionUpdater::HandleManifestResults(
522    const ManifestFetchData& fetch_data,
523    const UpdateManifest::Results& results) {
524
525  // Examine the parsed manifest and kick off fetches of any new crx files.
526  std::vector<int> updates = DetermineUpdates(fetch_data, results);
527  for (size_t i = 0; i < updates.size(); i++) {
528    const UpdateManifest::Result* update = &(results.list.at(updates[i]));
529    FetchUpdatedExtension(update->extension_id, update->crx_url,
530        update->package_hash, update->version);
531  }
532
533  // If the manifest response included a <daystart> element, we want to save
534  // that value for any extensions which had sent ping_days in the request.
535  if (fetch_data.base_url().DomainIs("google.com") &&
536      results.daystart_elapsed_seconds >= 0) {
537    Time daystart =
538      Time::Now() - TimeDelta::FromSeconds(results.daystart_elapsed_seconds);
539
540    const std::set<std::string>& extension_ids = fetch_data.extension_ids();
541    std::set<std::string>::const_iterator i;
542    for (i = extension_ids.begin(); i != extension_ids.end(); i++) {
543      bool did_ping = fetch_data.DidPing(*i);
544      if (did_ping) {
545        if (*i == kBlacklistAppID) {
546          service_->extension_prefs()->SetBlacklistLastPingDay(daystart);
547        } else if (service_->GetExtensionById(*i, true) != NULL) {
548          service_->extension_prefs()->SetLastPingDay(*i, daystart);
549        }
550      }
551    }
552  }
553}
554
555void ExtensionUpdater::ProcessBlacklist(const std::string& data) {
556  // Verify sha256 hash value.
557  char sha256_hash_value[base::SHA256_LENGTH];
558  base::SHA256HashString(data, sha256_hash_value, base::SHA256_LENGTH);
559  std::string hash_in_hex = HexEncode(sha256_hash_value, base::SHA256_LENGTH);
560
561  if (current_extension_fetch_.package_hash != hash_in_hex) {
562    NOTREACHED() << "Fetched blacklist checksum is not as expected. "
563      << "Expected: " << current_extension_fetch_.package_hash
564      << " Actual: " << hash_in_hex;
565    return;
566  }
567  std::vector<std::string> blacklist;
568  SplitString(data, '\n', &blacklist);
569
570  // Tell ExtensionService to update prefs.
571  service_->UpdateExtensionBlacklist(blacklist);
572
573  // Update the pref value for blacklist version
574  prefs_->SetString(kExtensionBlacklistUpdateVersion,
575                    current_extension_fetch_.version);
576  prefs_->ScheduleSavePersistentPrefs();
577}
578
579void ExtensionUpdater::OnCRXFetchComplete(const GURL& url,
580                                          const URLRequestStatus& status,
581                                          int response_code,
582                                          const std::string& data) {
583  if (status.status() == URLRequestStatus::SUCCESS &&
584      response_code == 200) {
585    if (current_extension_fetch_.id == kBlacklistAppID) {
586      ProcessBlacklist(data);
587    } else {
588      // Successfully fetched - now write crx to a file so we can have the
589      // ExtensionsService install it.
590      ChromeThread::PostTask(
591          ChromeThread::FILE, FROM_HERE,
592          NewRunnableMethod(
593              file_handler_.get(), &ExtensionUpdaterFileHandler::WriteTempFile,
594              current_extension_fetch_.id, data, url,
595              make_scoped_refptr(this)));
596    }
597  } else {
598    // TODO(asargent) do things like exponential backoff, handling
599    // 503 Service Unavailable / Retry-After headers, etc. here.
600    // (http://crbug.com/12546).
601    LOG(INFO) << "Failed to fetch extension '" <<
602        url.possibly_invalid_spec() << "' response code:" << response_code;
603  }
604  extension_fetcher_.reset();
605  current_extension_fetch_ = ExtensionFetch();
606
607  // If there are any pending downloads left, start one.
608  if (extensions_pending_.size() > 0) {
609    ExtensionFetch next = extensions_pending_.front();
610    extensions_pending_.pop_front();
611    FetchUpdatedExtension(next.id, next.url, next.package_hash, next.version);
612  }
613}
614
615void ExtensionUpdater::OnCRXFileWritten(const std::string& id,
616                                        const FilePath& path,
617                                        const GURL& download_url) {
618  // The ExtensionsService is now responsible for cleaning up the temp file
619  // at |path|.
620  service_->UpdateExtension(id, path, download_url);
621}
622
623
624void ExtensionUpdater::ScheduleNextCheck(const TimeDelta& target_delay) {
625  DCHECK(!timer_.IsRunning());
626  DCHECK(target_delay >= TimeDelta::FromSeconds(1));
627
628  // Add +/- 10% random jitter.
629  double delay_ms = target_delay.InMillisecondsF();
630  double jitter_factor = (RandDouble() * .2) - 0.1;
631  delay_ms += delay_ms * jitter_factor;
632  TimeDelta actual_delay = TimeDelta::FromMilliseconds(
633      static_cast<int64>(delay_ms));
634
635  // Save the time of next check.
636  Time next = Time::Now() + actual_delay;
637  prefs_->SetInt64(kNextExtensionsUpdateCheck, next.ToInternalValue());
638  prefs_->ScheduleSavePersistentPrefs();
639
640  timer_.Start(actual_delay, this, &ExtensionUpdater::TimerFired);
641}
642
643void ExtensionUpdater::TimerFired() {
644  CheckNow();
645
646  // If the user has overridden the update frequency, don't bother reporting
647  // this.
648  if (frequency_seconds_ == ExtensionsService::kDefaultUpdateFrequencySeconds) {
649    Time last = Time::FromInternalValue(prefs_->GetInt64(
650        kLastExtensionsUpdateCheck));
651    if (last.ToInternalValue() != 0) {
652      // Use counts rather than time so we can use minutes rather than millis.
653      UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.UpdateCheckGap",
654          (Time::Now() - last).InMinutes(),
655          base::TimeDelta::FromSeconds(kStartupWaitSeconds).InMinutes(),
656          base::TimeDelta::FromDays(40).InMinutes(),
657          50);  // 50 buckets seems to be the default.
658    }
659  }
660
661  // Save the last check time, and schedule the next check.
662  int64 now = Time::Now().ToInternalValue();
663  prefs_->SetInt64(kLastExtensionsUpdateCheck, now);
664  ScheduleNextCheck(TimeDelta::FromSeconds(frequency_seconds_));
665}
666
667void ExtensionUpdater::CheckNow() {
668  ManifestFetchesBuilder fetches_builder(service_);
669
670  const ExtensionList* extensions = service_->extensions();
671  for (ExtensionList::const_iterator iter = extensions->begin();
672       iter != extensions->end(); ++iter) {
673    fetches_builder.AddExtension(**iter);
674  }
675
676  const PendingExtensionMap& pending_extensions =
677      service_->pending_extensions();
678  for (PendingExtensionMap::const_iterator iter = pending_extensions.begin();
679       iter != pending_extensions.end(); ++iter) {
680    fetches_builder.AddPendingExtension(iter->first, iter->second);
681  }
682
683  fetches_builder.ReportStats();
684
685  std::vector<ManifestFetchData*> fetches(fetches_builder.GetFetches());
686
687  // Start a fetch of the blacklist if needed.
688  if (blacklist_checks_enabled_ && service_->HasInstalledExtensions()) {
689    ManifestFetchData* blacklist_fetch =
690        new ManifestFetchData(GURL(kBlacklistUpdateUrl));
691    std::string version = prefs_->GetString(kExtensionBlacklistUpdateVersion);
692    int ping_days =
693        CalculatePingDays(service_->extension_prefs()->BlacklistLastPingDay());
694    blacklist_fetch->AddExtension(kBlacklistAppID, version, ping_days);
695    StartUpdateCheck(blacklist_fetch);
696  }
697
698  // Now start fetching regular extension updates
699  for (std::vector<ManifestFetchData*>::const_iterator it = fetches.begin();
700       it != fetches.end(); ++it) {
701    // StartUpdateCheck makes sure the url isn't already downloading or
702    // scheduled, so we don't need to check before calling it. Ownership of
703    // fetch is transferred here.
704    StartUpdateCheck(*it);
705  }
706  // We don't want to use fetches after this since StartUpdateCheck()
707  // takes ownership of its argument.
708  fetches.clear();
709}
710
711bool ExtensionUpdater::GetExistingVersion(const std::string& id,
712                                          std::string* version) {
713  if (id == kBlacklistAppID) {
714    *version = prefs_->GetString(kExtensionBlacklistUpdateVersion);
715    return true;
716  }
717  Extension* extension = service_->GetExtensionById(id, false);
718  if (!extension) {
719    return false;
720  }
721  *version = extension->version()->GetString();
722  return true;
723}
724
725std::vector<int> ExtensionUpdater::DetermineUpdates(
726    const ManifestFetchData& fetch_data,
727    const UpdateManifest::Results& possible_updates) {
728  std::vector<int> result;
729
730  // This will only get set if one of possible_updates specifies
731  // browser_min_version.
732  scoped_ptr<Version> browser_version;
733
734  for (size_t i = 0; i < possible_updates.list.size(); i++) {
735    const UpdateManifest::Result* update = &possible_updates.list[i];
736
737    if (!fetch_data.Includes(update->extension_id))
738      continue;
739
740    if (service_->pending_extensions().find(update->extension_id) ==
741        service_->pending_extensions().end()) {
742      // If we're not installing pending extension, and the update
743      // version is the same or older than what's already installed,
744      // we don't want it.
745      std::string version;
746      if (!GetExistingVersion(update->extension_id, &version))
747        continue;
748
749      scoped_ptr<Version> existing_version(
750          Version::GetVersionFromString(version));
751      scoped_ptr<Version> update_version(
752          Version::GetVersionFromString(update->version));
753
754      if (!update_version.get() ||
755          update_version->CompareTo(*(existing_version.get())) <= 0) {
756        continue;
757      }
758    }
759
760    // If the update specifies a browser minimum version, do we qualify?
761    if (update->browser_min_version.length() > 0) {
762      // First determine the browser version if we haven't already.
763      if (!browser_version.get()) {
764        scoped_ptr<FileVersionInfo> version_info(
765            chrome::GetChromeVersionInfo());
766        if (version_info.get()) {
767          browser_version.reset(Version::GetVersionFromString(
768              version_info->product_version()));
769        }
770      }
771      scoped_ptr<Version> browser_min_version(
772          Version::GetVersionFromString(update->browser_min_version));
773      if (browser_version.get() && browser_min_version.get() &&
774          browser_min_version->CompareTo(*browser_version.get()) > 0) {
775        // TODO(asargent) - We may want this to show up in the extensions UI
776        // eventually. (http://crbug.com/12547).
777        LOG(WARNING) << "Updated version of extension " << update->extension_id
778          << " available, but requires chrome version "
779          << update->browser_min_version;
780
781        continue;
782      }
783    }
784    result.push_back(i);
785  }
786  return result;
787}
788
789void ExtensionUpdater::StartUpdateCheck(ManifestFetchData* fetch_data) {
790  std::deque<ManifestFetchData*>::const_iterator i;
791  for (i = manifests_pending_.begin(); i != manifests_pending_.end(); i++) {
792    if (fetch_data->full_url() == (*i)->full_url()) {
793      // This url is already scheduled to be fetched.
794      delete fetch_data;
795      return;
796    }
797  }
798
799  if (manifest_fetcher_.get() != NULL) {
800    if (manifest_fetcher_->url() != fetch_data->full_url()) {
801      manifests_pending_.push_back(fetch_data);
802    }
803  } else {
804    UMA_HISTOGRAM_COUNTS("Extensions.UpdateCheckUrlLength",
805        fetch_data->full_url().possibly_invalid_spec().length());
806
807    current_manifest_fetch_.reset(fetch_data);
808    manifest_fetcher_.reset(
809        URLFetcher::Create(kManifestFetcherId, fetch_data->full_url(),
810                           URLFetcher::GET, this));
811    manifest_fetcher_->set_request_context(Profile::GetDefaultRequestContext());
812    manifest_fetcher_->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES |
813                                      net::LOAD_DO_NOT_SAVE_COOKIES);
814    manifest_fetcher_->Start();
815  }
816}
817
818void ExtensionUpdater::FetchUpdatedExtension(const std::string& id,
819                                             const GURL& url,
820                                             const std::string& hash,
821                                             const std::string& version) {
822  for (std::deque<ExtensionFetch>::const_iterator iter =
823           extensions_pending_.begin();
824       iter != extensions_pending_.end(); ++iter) {
825    if (iter->id == id || iter->url == url) {
826      return;  // already scheduled
827    }
828  }
829
830  if (extension_fetcher_.get() != NULL) {
831    if (extension_fetcher_->url() != url) {
832      extensions_pending_.push_back(ExtensionFetch(id, url, hash, version));
833    }
834  } else {
835    extension_fetcher_.reset(
836        URLFetcher::Create(kExtensionFetcherId, url, URLFetcher::GET, this));
837    extension_fetcher_->set_request_context(
838        Profile::GetDefaultRequestContext());
839    extension_fetcher_->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES |
840                                       net::LOAD_DO_NOT_SAVE_COOKIES);
841    extension_fetcher_->Start();
842    current_extension_fetch_ = ExtensionFetch(id, url, hash, version);
843  }
844}
845