blob: cac5efef1fee4718ebeaf7c2f384149e67187153 [file] [log] [blame]
[email protected]f1050432012-02-15 01:35:461// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]e8f96ff2011-08-03 05:07:332// 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/component_updater/component_updater_service.h"
6
7#include <algorithm>
8#include <vector>
9
10#include "base/at_exit.h"
[email protected]44da56e2011-11-21 19:59:1411#include "base/bind.h"
[email protected]e8f96ff2011-08-03 05:07:3312#include "base/file_path.h"
13#include "base/file_util.h"
14#include "base/logging.h"
[email protected]7226b33c2011-08-18 08:44:2215#include "base/memory/scoped_ptr.h"
[email protected]e8f96ff2011-08-03 05:07:3316#include "base/stl_util.h"
[email protected]eb72b272011-12-19 16:10:5517#include "base/string_piece.h"
[email protected]e8f96ff2011-08-03 05:07:3318#include "base/string_util.h"
19#include "base/stringprintf.h"
[email protected]3ea1b182013-02-08 22:38:4120#include "base/strings/string_number_conversions.h"
[email protected]e8f96ff2011-08-03 05:07:3321#include "base/timer.h"
22#include "chrome/browser/browser_process.h"
23#include "chrome/browser/component_updater/component_unpacker.h"
24#include "chrome/common/chrome_notification_types.h"
25#include "chrome/common/chrome_utility_messages.h"
26#include "chrome/common/chrome_version_info.h"
27#include "chrome/common/extensions/extension.h"
[email protected]b7b63872013-01-03 02:41:1928#include "content/public/browser/browser_thread.h"
[email protected]ad50def52011-10-19 23:17:0729#include "content/public/browser/notification_service.h"
[email protected]c4f883a2012-02-03 17:02:0730#include "content/public/browser/utility_process_host.h"
31#include "content/public/browser/utility_process_host_client.h"
[email protected]e8f96ff2011-08-03 05:07:3332#include "googleurl/src/gurl.h"
33#include "net/base/escape.h"
34#include "net/base/load_flags.h"
[email protected]3dc1bc42012-06-19 08:20:5335#include "net/url_request/url_fetcher.h"
[email protected]15fb2aa2012-05-22 22:52:5936#include "net/url_request/url_fetcher_delegate.h"
[email protected]9d8ea302012-09-25 15:04:2237#include "net/url_request/url_request_status.h"
[email protected]e8f96ff2011-08-03 05:07:3338
[email protected]631bb742011-11-02 11:29:3939using content::BrowserThread;
[email protected]c4f883a2012-02-03 17:02:0740using content::UtilityProcessHost;
41using content::UtilityProcessHostClient;
[email protected]1c321ee52012-05-21 03:02:3442using extensions::Extension;
[email protected]631bb742011-11-02 11:29:3943
[email protected]44da56e2011-11-21 19:59:1444// The component updater is designed to live until process shutdown, so
45// base::Bind() calls are not refcounted.
46
[email protected]e8f96ff2011-08-03 05:07:3347namespace {
[email protected]dc06f0b2013-01-23 20:03:1648// Manifest sources, from most important to least important.
49const CrxComponent::UrlSource kManifestSources[] = {
50 CrxComponent::BANDAID,
51 CrxComponent::CWS_PUBLIC,
52 CrxComponent::CWS_SANDBOX
53};
54
[email protected]e8f96ff2011-08-03 05:07:3355// Extends an omaha compatible update check url |query| string. Does
56// not mutate the string if it would be longer than |limit| chars.
[email protected]4c37b452011-12-21 01:33:5257bool AddQueryString(const std::string& id,
58 const std::string& version,
59 size_t limit,
60 std::string* query) {
[email protected]e8f96ff2011-08-03 05:07:3361 std::string additional =
62 base::StringPrintf("id=%s&v=%s&uc", id.c_str(), version.c_str());
[email protected]4a19be92011-09-22 14:25:0263 additional = "x=" + net::EscapeQueryParamValue(additional, true);
[email protected]e8f96ff2011-08-03 05:07:3364 if ((additional.size() + query->size() + 1) > limit)
65 return false;
[email protected]926d36332011-10-05 01:06:2566 if (!query->empty())
67 query->append(1, '&');
[email protected]e8f96ff2011-08-03 05:07:3368 query->append(additional);
69 return true;
70}
71
[email protected]926d36332011-10-05 01:06:2572// Create the final omaha compatible query. The |extra| is optional and can
73// be null. It should contain top level (non-escaped) parameters.
74std::string MakeFinalQuery(const std::string& host,
75 const std::string& query,
76 const char* extra) {
77 std::string request(host);
78 request.append(1, '?');
79 if (extra) {
80 request.append(extra);
81 request.append(1, '&');
82 }
83 request.append(query);
84 return request;
85}
86
[email protected]e8f96ff2011-08-03 05:07:3387// Produces an extension-like friendly |id|. This might be removed in the
88// future if we roll our on packing tools.
89static std::string HexStringToID(const std::string& hexstr) {
90 std::string id;
91 for (size_t i = 0; i < hexstr.size(); ++i) {
92 int val;
[email protected]eb72b272011-12-19 16:10:5593 if (base::HexStringToInt(base::StringPiece(hexstr.begin() + i,
94 hexstr.begin() + i + 1),
95 &val)) {
[email protected]e8f96ff2011-08-03 05:07:3396 id.append(1, val + 'a');
[email protected]eb72b272011-12-19 16:10:5597 } else {
[email protected]e8f96ff2011-08-03 05:07:3398 id.append(1, 'a');
[email protected]eb72b272011-12-19 16:10:5599 }
[email protected]e8f96ff2011-08-03 05:07:33100 }
101 DCHECK(Extension::IdIsValid(id));
102 return id;
103}
104
[email protected]360b8bb2011-09-01 21:48:06105// Returns given a crx id it returns a small number, less than 100, that has a
106// decent chance of being unique among the registered components. It also has
107// the nice property that can be trivially computed by hand.
108static int CrxIdtoUMAId(const std::string& id) {
[email protected]a1f8c162012-09-27 22:32:21109 CHECK_GT(id.size(), 2U);
[email protected]360b8bb2011-09-01 21:48:06110 return id[0] + id[1] + id[2] - ('a' * 3);
111}
112
[email protected]e8f96ff2011-08-03 05:07:33113// Helper to do version check for components.
114bool IsVersionNewer(const Version& current, const std::string& proposed) {
115 Version proposed_ver(proposed);
116 if (!proposed_ver.IsValid())
117 return false;
118 return (current.CompareTo(proposed_ver) < 0);
119}
120
121// Helper template class that allows our main class to have separate
[email protected]f1050432012-02-15 01:35:46122// OnURLFetchComplete() callbacks for different types of url requests
[email protected]e8f96ff2011-08-03 05:07:33123// they are differentiated by the |Ctx| type.
124template <typename Del, typename Ctx>
[email protected]15fb2aa2012-05-22 22:52:59125class DelegateWithContext : public net::URLFetcherDelegate {
[email protected]e8f96ff2011-08-03 05:07:33126 public:
127 DelegateWithContext(Del* delegate, Ctx* context)
128 : delegate_(delegate), context_(context) {}
129
[email protected]10c2d692012-05-11 05:32:23130 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
[email protected]e8f96ff2011-08-03 05:07:33131 delegate_->OnURLFetchComplete(source, context_);
132 delete this;
133 }
134
135 private:
136 ~DelegateWithContext() {}
137
138 Del* delegate_;
139 Ctx* context_;
140};
141// This function creates the right DelegateWithContext using template inference.
142template <typename Del, typename Ctx>
[email protected]15fb2aa2012-05-22 22:52:59143net::URLFetcherDelegate* MakeContextDelegate(Del* delegate, Ctx* context) {
[email protected]e8f96ff2011-08-03 05:07:33144 return new DelegateWithContext<Del, Ctx>(delegate, context);
145}
146
147// Helper to start a url request using |fetcher| with the common flags.
[email protected]d3ec669b2012-05-23 07:12:14148void StartFetch(net::URLFetcher* fetcher,
[email protected]e8f96ff2011-08-03 05:07:33149 net::URLRequestContextGetter* context_getter,
150 bool save_to_file) {
[email protected]7cc6e5632011-10-25 17:56:12151 fetcher->SetRequestContext(context_getter);
152 fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
153 net::LOAD_DO_NOT_SAVE_COOKIES |
154 net::LOAD_DISABLE_CACHE);
[email protected]e8f96ff2011-08-03 05:07:33155 // TODO(cpu): Define our retry and backoff policy.
[email protected]7cc6e5632011-10-25 17:56:12156 fetcher->SetAutomaticallyRetryOn5xx(false);
[email protected]e8f96ff2011-08-03 05:07:33157 if (save_to_file) {
158 fetcher->SaveResponseToTemporaryFile(
159 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
160 }
161 fetcher->Start();
162}
163
164// Returs true if the url request of |fetcher| was succesful.
[email protected]10c2d692012-05-11 05:32:23165bool FetchSuccess(const net::URLFetcher& fetcher) {
[email protected]7cc6e5632011-10-25 17:56:12166 return (fetcher.GetStatus().status() == net::URLRequestStatus::SUCCESS) &&
167 (fetcher.GetResponseCode() == 200);
[email protected]e8f96ff2011-08-03 05:07:33168}
169
170// This is the one and only per-item state structure. Designed to be hosted
171// in a std::vector or a std::list. The two main members are |component|
172// which is supplied by the the component updater client and |status| which
173// is modified as the item is processed by the update pipeline. The expected
174// transition graph is:
175// error error error
176// +--kNoUpdate<------<-------+------<------+------<------+
177// | | | |
178// V yes | | |
179// kNew --->kChecking-->[update?]----->kCanUpdate-->kDownloading-->kUpdating
180// ^ | |
181// | |no |
182// |--kUpToDate<---+ |
183// | success |
184// +--kUpdated<-------------------------------------------+
185//
186struct CrxUpdateItem {
187 enum Status {
188 kNew,
189 kChecking,
190 kCanUpdate,
191 kDownloading,
192 kUpdating,
193 kUpdated,
194 kUpToDate,
195 kNoUpdate,
196 kLastStatus
197 };
198
199 Status status;
200 GURL crx_url;
201 std::string id;
202 base::Time last_check;
203 CrxComponent component;
[email protected]07f93af12011-08-17 20:57:22204 Version next_version;
[email protected]e8f96ff2011-08-03 05:07:33205
206 CrxUpdateItem() : status(kNew) {}
207
208 // Function object used to find a specific component.
209 class FindById {
210 public:
211 explicit FindById(const std::string& id) : id_(id) {}
212
213 bool operator() (CrxUpdateItem* item) const {
214 return (item->id == id_);
215 }
216 private:
217 const std::string& id_;
218 };
219};
220
221} // namespace.
222
223typedef ComponentUpdateService::Configurator Config;
224
[email protected]dc06f0b2013-01-23 20:03:16225CrxComponent::CrxComponent()
226 : installer(NULL),
227 source(BANDAID) {
228}
229
230CrxComponent::~CrxComponent() {
231}
[email protected]e8f96ff2011-08-03 05:07:33232
233//////////////////////////////////////////////////////////////////////////////
234// The one and only implementation of the ComponentUpdateService interface. In
235// charge of running the show. The main method is ProcessPendingItems() which
[email protected]50b01392012-09-01 03:33:13236// is called periodically to do the upgrades/installs or the update checks.
[email protected]e8f96ff2011-08-03 05:07:33237// An important consideration here is to be as "low impact" as we can to the
238// rest of the browser, so even if we have many components registered and
[email protected]f1050432012-02-15 01:35:46239// eligible for update, we only do one thing at a time with pauses in between
[email protected]e8f96ff2011-08-03 05:07:33240// the tasks. Also when we do network requests there is only one |url_fetcher_|
241// in flight at at a time.
242// There are no locks in this code, the main structure |work_items_| is mutated
243// only from the UI thread. The unpack and installation is done in the file
244// thread and the network requests are done in the IO thread and in the file
245// thread.
246class CrxUpdateService : public ComponentUpdateService {
247 public:
248 explicit CrxUpdateService(ComponentUpdateService::Configurator* config);
249
250 virtual ~CrxUpdateService();
251
252 // Overrides for ComponentUpdateService.
253 virtual Status Start() OVERRIDE;
254 virtual Status Stop() OVERRIDE;
255 virtual Status RegisterComponent(const CrxComponent& component) OVERRIDE;
256
257 // The only purpose of this class is to forward the
[email protected]c4f883a2012-02-03 17:02:07258 // UtilityProcessHostClient callbacks so CrxUpdateService does
[email protected]e8f96ff2011-08-03 05:07:33259 // not have to derive from it because that is refcounted.
[email protected]c4f883a2012-02-03 17:02:07260 class ManifestParserBridge : public UtilityProcessHostClient {
[email protected]e8f96ff2011-08-03 05:07:33261 public:
262 explicit ManifestParserBridge(CrxUpdateService* service)
263 : service_(service) {}
264
265 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
266 bool handled = true;
267 IPC_BEGIN_MESSAGE_MAP(ManifestParserBridge, message)
[email protected]2ccf45c2011-08-19 23:35:50268 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseUpdateManifest_Succeeded,
[email protected]e8f96ff2011-08-03 05:07:33269 OnParseUpdateManifestSucceeded)
[email protected]2ccf45c2011-08-19 23:35:50270 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseUpdateManifest_Failed,
[email protected]e8f96ff2011-08-03 05:07:33271 OnParseUpdateManifestFailed)
272 IPC_MESSAGE_UNHANDLED(handled = false)
273 IPC_END_MESSAGE_MAP()
274 return handled;
275 }
276
277 private:
[email protected]a3988cc2012-04-27 05:07:18278 virtual ~ManifestParserBridge() {}
279
[email protected]f1050432012-02-15 01:35:46280 // Omaha update response XML was successfully parsed.
[email protected]e8f96ff2011-08-03 05:07:33281 void OnParseUpdateManifestSucceeded(const UpdateManifest::Results& r) {
282 service_->OnParseUpdateManifestSucceeded(r);
283 }
284 // Omaha update response XML could not be parsed.
285 void OnParseUpdateManifestFailed(const std::string& e) {
286 service_->OnParseUpdateManifestFailed(e);
287 }
288
289 CrxUpdateService* service_;
290 DISALLOW_COPY_AND_ASSIGN(ManifestParserBridge);
291 };
292
293 // Context for a update check url request. See DelegateWithContext above.
294 struct UpdateContext {
295 base::Time start;
296 UpdateContext() : start(base::Time::Now()) {}
297 };
298
299 // Context for a crx download url request. See DelegateWithContext above.
300 struct CRXContext {
301 ComponentInstaller* installer;
302 std::vector<uint8> pk_hash;
303 std::string id;
304 CRXContext() : installer(NULL) {}
305 };
306
[email protected]10c2d692012-05-11 05:32:23307 void OnURLFetchComplete(const net::URLFetcher* source,
[email protected]7cc6e5632011-10-25 17:56:12308 UpdateContext* context);
[email protected]e8f96ff2011-08-03 05:07:33309
[email protected]10c2d692012-05-11 05:32:23310 void OnURLFetchComplete(const net::URLFetcher* source,
[email protected]7cc6e5632011-10-25 17:56:12311 CRXContext* context);
[email protected]e8f96ff2011-08-03 05:07:33312
313 private:
314 // See ManifestParserBridge.
315 void OnParseUpdateManifestSucceeded(
316 const UpdateManifest::Results& results);
317
318 // See ManifestParserBridge.
319 void OnParseUpdateManifestFailed(
320 const std::string& error_message);
321
322 bool AddItemToUpdateCheck(CrxUpdateItem* item, std::string* query);
323
324 void ProcessPendingItems();
325
326 void ScheduleNextRun(bool step_delay);
327
328 void ParseManifest(const std::string& xml);
329
[email protected]650b2d52013-02-10 03:41:45330 void Install(const CRXContext* context, const base::FilePath& crx_path);
[email protected]e8f96ff2011-08-03 05:07:33331
[email protected]07f93af12011-08-17 20:57:22332 void DoneInstalling(const std::string& component_id,
333 ComponentUnpacker::Error error);
[email protected]e8f96ff2011-08-03 05:07:33334
335 size_t ChangeItemStatus(CrxUpdateItem::Status from,
336 CrxUpdateItem::Status to);
337
338 CrxUpdateItem* FindUpdateItemById(const std::string& id);
339
340 scoped_ptr<Config> config_;
341
[email protected]d3ec669b2012-05-23 07:12:14342 scoped_ptr<net::URLFetcher> url_fetcher_;
[email protected]e8f96ff2011-08-03 05:07:33343
344 typedef std::vector<CrxUpdateItem*> UpdateItems;
345 UpdateItems work_items_;
346
347 base::OneShotTimer<CrxUpdateService> timer_;
348
349 Version chrome_version_;
350
351 bool running_;
352
353 DISALLOW_COPY_AND_ASSIGN(CrxUpdateService);
354};
355
[email protected]e8f96ff2011-08-03 05:07:33356//////////////////////////////////////////////////////////////////////////////
357
358CrxUpdateService::CrxUpdateService(
359 ComponentUpdateService::Configurator* config)
360 : config_(config),
361 chrome_version_(chrome::VersionInfo().Version()),
362 running_(false) {
363}
364
365CrxUpdateService::~CrxUpdateService() {
366 // Because we are a singleton, at this point only the UI thread should be
367 // alive, this simplifies the management of the work that could be in
368 // flight in other threads.
369 Stop();
370 STLDeleteElements(&work_items_);
371}
372
373ComponentUpdateService::Status CrxUpdateService::Start() {
374 // Note that RegisterComponent will call Start() when the first
375 // component is registered, so it can be called twice. This way
376 // we avoid scheduling the timer if there is no work to do.
377 running_ = true;
378 if (work_items_.empty())
379 return kOk;
380
[email protected]ad50def52011-10-19 23:17:07381 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33382 chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED,
[email protected]6c2381d2011-10-19 02:52:53383 content::Source<ComponentUpdateService>(this),
[email protected]ad50def52011-10-19 23:17:07384 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33385
[email protected]d323a172011-09-02 18:23:02386 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(config_->InitialDelay()),
[email protected]e8f96ff2011-08-03 05:07:33387 this, &CrxUpdateService::ProcessPendingItems);
388 return kOk;
389}
390
391// Stop the main check + update loop. In flight operations will be
392// completed.
393ComponentUpdateService::Status CrxUpdateService::Stop() {
394 running_ = false;
395 timer_.Stop();
396 return kOk;
397}
398
399// This function sets the timer which will call ProcessPendingItems() there
400// are two kind of waits, the short one (with step_delay = true) and the
401// long one.
402void CrxUpdateService::ScheduleNextRun(bool step_delay) {
403 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]cf442612011-08-09 20:20:12404 DCHECK(url_fetcher_.get() == NULL);
[email protected]e8f96ff2011-08-03 05:07:33405 CHECK(!timer_.IsRunning());
406 // It could be the case that Stop() had been called while a url request
407 // or unpacking was in flight, if so we arrive here but |running_| is
408 // false. In that case do not loop again.
409 if (!running_)
410 return;
411
[email protected]cf442612011-08-09 20:20:12412 int64 delay = step_delay ? config_->StepDelay() : config_->NextCheckDelay();
413
[email protected]e8f96ff2011-08-03 05:07:33414 if (!step_delay) {
[email protected]ad50def52011-10-19 23:17:07415 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33416 chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING,
[email protected]6c2381d2011-10-19 02:52:53417 content::Source<ComponentUpdateService>(this),
[email protected]ad50def52011-10-19 23:17:07418 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33419 // Zero is only used for unit tests.
[email protected]cf442612011-08-09 20:20:12420 if (0 == delay)
[email protected]e8f96ff2011-08-03 05:07:33421 return;
422 }
423
[email protected]d323a172011-09-02 18:23:02424 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(delay),
[email protected]e8f96ff2011-08-03 05:07:33425 this, &CrxUpdateService::ProcessPendingItems);
426}
427
428// Given a extension-like component id, find the associated component.
429CrxUpdateItem* CrxUpdateService::FindUpdateItemById(const std::string& id) {
430 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
431 CrxUpdateItem::FindById finder(id);
432 UpdateItems::iterator it = std::find_if(work_items_.begin(),
433 work_items_.end(),
434 finder);
435 if (it == work_items_.end())
436 return NULL;
437 return (*it);
438}
439
440// Changes all the components in |work_items_| that have |from| status to
441// |to| statatus and returns how many have been changed.
442size_t CrxUpdateService::ChangeItemStatus(CrxUpdateItem::Status from,
443 CrxUpdateItem::Status to) {
444 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
445 size_t count = 0;
446 for (UpdateItems::iterator it = work_items_.begin();
447 it != work_items_.end(); ++it) {
448 CrxUpdateItem* item = *it;
449 if (item->status != from)
450 continue;
451 item->status = to;
452 ++count;
453 }
454 return count;
455}
456
457// Adds a component to be checked for upgrades. If the component exists it
458// it will be replaced and the return code is kReplaced.
459//
460// TODO(cpu): Evaluate if we want to support un-registration.
461ComponentUpdateService::Status CrxUpdateService::RegisterComponent(
462 const CrxComponent& component) {
463 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
464 if (component.pk_hash.empty() ||
465 !component.version.IsValid() ||
466 !component.installer)
467 return kError;
468
469 std::string id =
470 HexStringToID(StringToLowerASCII(base::HexEncode(&component.pk_hash[0],
471 component.pk_hash.size()/2)));
472 CrxUpdateItem* uit;
473 uit = FindUpdateItemById(id);
474 if (uit) {
475 uit->component = component;
476 return kReplaced;
477 }
478
479 uit = new CrxUpdateItem;
480 uit->id.swap(id);
481 uit->component = component;
482 work_items_.push_back(uit);
483 // If this is the first component registered we call Start to
484 // schedule the first timer.
485 if (running_ && (work_items_.size() == 1))
486 Start();
487
488 return kOk;
489}
490
491// Sets a component to be checked for updates.
492// The componet to add is |crxit| and the |query| string is modified with the
493// required omaha compatible query. Returns false when the query strings
494// is longer than specified by UrlSizeLimit().
495bool CrxUpdateService::AddItemToUpdateCheck(CrxUpdateItem* item,
496 std::string* query) {
497 if (!AddQueryString(item->id,
498 item->component.version.GetString(),
499 config_->UrlSizeLimit(), query))
500 return false;
501 item->status = CrxUpdateItem::kChecking;
502 item->last_check = base::Time::Now();
503 return true;
504}
505
506// Here is where the work gets scheduled. Given that our |work_items_| list
507// is expected to be ten or less items, we simply loop several times.
508void CrxUpdateService::ProcessPendingItems() {
509 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
510 // First check for ready upgrades and do one. The first
511 // step is to fetch the crx package.
512 for (UpdateItems::const_iterator it = work_items_.begin();
513 it != work_items_.end(); ++it) {
514 CrxUpdateItem* item = *it;
515 if (item->status != CrxUpdateItem::kCanUpdate)
516 continue;
517 // Found component to update, start the process.
518 item->status = CrxUpdateItem::kDownloading;
519 CRXContext* context = new CRXContext;
520 context->pk_hash = item->component.pk_hash;
521 context->id = item->id;
522 context->installer = item->component.installer;
[email protected]3dc1bc42012-06-19 08:20:53523 url_fetcher_.reset(net::URLFetcher::Create(
[email protected]d3ec669b2012-05-23 07:12:14524 0, item->crx_url, net::URLFetcher::GET,
[email protected]e8f96ff2011-08-03 05:07:33525 MakeContextDelegate(this, context)));
526 StartFetch(url_fetcher_.get(), config_->RequestContext(), true);
527 return;
528 }
529
[email protected]dc06f0b2013-01-23 20:03:16530 for (size_t ix = 0; ix != arraysize(kManifestSources); ++ix) {
531 const CrxComponent::UrlSource manifest_source = kManifestSources[ix];
[email protected]e8f96ff2011-08-03 05:07:33532
[email protected]dc06f0b2013-01-23 20:03:16533 std::string query;
534 // If no pending upgrades, we check if there are new components we have not
535 // checked against the server. We can batch some in a single url request.
536 for (UpdateItems::const_iterator it = work_items_.begin();
537 it != work_items_.end(); ++it) {
538 CrxUpdateItem* item = *it;
539 if (item->status != CrxUpdateItem::kNew)
540 continue;
541 if (item->component.source != manifest_source)
542 continue;
543 if (!AddItemToUpdateCheck(item, &query))
544 break;
545 }
[email protected]e8f96ff2011-08-03 05:07:33546
[email protected]dc06f0b2013-01-23 20:03:16547 // Next we can go back to components we already checked, here
548 // we can also batch them in a single url request, as long as
549 // we have not checked them recently.
550 const base::TimeDelta min_delta_time =
551 base::TimeDelta::FromSeconds(config_->MinimumReCheckWait());
[email protected]e8f96ff2011-08-03 05:07:33552
[email protected]dc06f0b2013-01-23 20:03:16553 for (UpdateItems::const_iterator it = work_items_.begin();
554 it != work_items_.end(); ++it) {
555 CrxUpdateItem* item = *it;
556 if ((item->status != CrxUpdateItem::kNoUpdate) &&
557 (item->status != CrxUpdateItem::kUpToDate))
558 continue;
559 if (item->component.source != manifest_source)
560 continue;
561 base::TimeDelta delta = base::Time::Now() - item->last_check;
562 if (delta < min_delta_time)
563 continue;
564 if (!AddItemToUpdateCheck(item, &query))
565 break;
566 }
567
568 // Finally, we check components that we already updated as long as
569 // we have not checked them recently.
570 for (UpdateItems::const_iterator it = work_items_.begin();
571 it != work_items_.end(); ++it) {
572 CrxUpdateItem* item = *it;
573 if (item->status != CrxUpdateItem::kUpdated)
574 continue;
575 if (item->component.source != manifest_source)
576 continue;
577 base::TimeDelta delta = base::Time::Now() - item->last_check;
578 if (delta < min_delta_time)
579 continue;
580 if (!AddItemToUpdateCheck(item, &query))
581 break;
582 }
583
584 // If no components to update we move down to the next source.
585 if (query.empty())
586 continue;
587
588 // We got components to check. Start the url request and exit.
589 const std::string full_query =
590 MakeFinalQuery(config_->UpdateUrl(manifest_source).spec(),
591 query,
592 config_->ExtraRequestParams());
593
594 url_fetcher_.reset(net::URLFetcher::Create(
595 0, GURL(full_query), net::URLFetcher::GET,
596 MakeContextDelegate(this, new UpdateContext())));
597 StartFetch(url_fetcher_.get(), config_->RequestContext(), false);
[email protected]e8f96ff2011-08-03 05:07:33598 return;
599 }
600
[email protected]dc06f0b2013-01-23 20:03:16601 // No components to update. Next check after the long sleep.
602 ScheduleNextRun(false);
[email protected]e8f96ff2011-08-03 05:07:33603}
604
605// Caled when we got a response from the update server. It consists of an xml
606// document following the omaha update scheme.
[email protected]10c2d692012-05-11 05:32:23607void CrxUpdateService::OnURLFetchComplete(const net::URLFetcher* source,
[email protected]07f93af12011-08-17 20:57:22608 UpdateContext* context) {
[email protected]e8f96ff2011-08-03 05:07:33609 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
610 if (FetchSuccess(*source)) {
611 std::string xml;
612 source->GetResponseAsString(&xml);
[email protected]cf442612011-08-09 20:20:12613 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33614 ParseManifest(xml);
615 } else {
[email protected]cf442612011-08-09 20:20:12616 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33617 CrxUpdateService::OnParseUpdateManifestFailed("network error");
618 }
[email protected]07f93af12011-08-17 20:57:22619 delete context;
[email protected]e8f96ff2011-08-03 05:07:33620}
621
622// Parsing the manifest is either done right now for tests or in a sandboxed
623// process for the production environment. This mitigates the case where an
624// attacker was able to feed us a malicious xml string.
625void CrxUpdateService::ParseManifest(const std::string& xml) {
626 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
627 if (config_->InProcess()) {
628 UpdateManifest manifest;
629 if (!manifest.Parse(xml)) {
630 CrxUpdateService::OnParseUpdateManifestFailed(manifest.errors());
631 } else {
632 CrxUpdateService::OnParseUpdateManifestSucceeded(manifest.results());
633 }
634 } else {
[email protected]c4f883a2012-02-03 17:02:07635 UtilityProcessHost* host = UtilityProcessHost::Create(
[email protected]7f8f24f2012-11-15 19:40:14636 new ManifestParserBridge(this),
637 base::MessageLoopProxy::current());
[email protected]c4f883a2012-02-03 17:02:07638 host->EnableZygote();
[email protected]2ccf45c2011-08-19 23:35:50639 host->Send(new ChromeUtilityMsg_ParseUpdateManifest(xml));
[email protected]e8f96ff2011-08-03 05:07:33640 }
641}
642
643// A valid Omaha update check has arrived, from only the list of components that
644// we are currently upgrading we check for a match in which the server side
645// version is newer, if so we queue them for an upgrade. The next time we call
646// ProcessPendingItems() one of them will be drafted for the upgrade process.
647void CrxUpdateService::OnParseUpdateManifestSucceeded(
648 const UpdateManifest::Results& results) {
649 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]a1f8c162012-09-27 22:32:21650 size_t update_pending = 0;
[email protected]e8f96ff2011-08-03 05:07:33651 std::vector<UpdateManifest::Result>::const_iterator it;
652 for (it = results.list.begin(); it != results.list.end(); ++it) {
653 CrxUpdateItem* crx = FindUpdateItemById(it->extension_id);
654 if (!crx)
655 continue;
656
657 if (crx->status != CrxUpdateItem::kChecking)
658 continue; // Not updating this component now.
659
[email protected]360b8bb2011-09-01 21:48:06660 config_->OnEvent(Configurator::kManifestCheck, CrxIdtoUMAId(crx->id));
661
[email protected]e8f96ff2011-08-03 05:07:33662 if (it->version.empty()) {
[email protected]cf442612011-08-09 20:20:12663 // No version means no update available.
[email protected]e8f96ff2011-08-03 05:07:33664 crx->status = CrxUpdateItem::kNoUpdate;
[email protected]cf442612011-08-09 20:20:12665 continue;
[email protected]e8f96ff2011-08-03 05:07:33666 }
667 if (!IsVersionNewer(crx->component.version, it->version)) {
[email protected]cf442612011-08-09 20:20:12668 // Our component is up to date.
[email protected]e8f96ff2011-08-03 05:07:33669 crx->status = CrxUpdateItem::kUpToDate;
[email protected]cf442612011-08-09 20:20:12670 continue;
[email protected]e8f96ff2011-08-03 05:07:33671 }
672 if (!it->browser_min_version.empty()) {
[email protected]cf442612011-08-09 20:20:12673 if (IsVersionNewer(chrome_version_, it->browser_min_version)) {
674 // Does not apply for this chrome version.
675 crx->status = CrxUpdateItem::kNoUpdate;
676 continue;
677 }
[email protected]e8f96ff2011-08-03 05:07:33678 }
679 // All test passed. Queue an upgrade for this component and fire the
680 // notifications.
681 crx->crx_url = it->crx_url;
682 crx->status = CrxUpdateItem::kCanUpdate;
[email protected]07f93af12011-08-17 20:57:22683 crx->next_version = Version(it->version);
[email protected]e8f96ff2011-08-03 05:07:33684 ++update_pending;
685
[email protected]ad50def52011-10-19 23:17:07686 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33687 chrome::NOTIFICATION_COMPONENT_UPDATE_FOUND,
[email protected]6c2381d2011-10-19 02:52:53688 content::Source<std::string>(&crx->id),
[email protected]ad50def52011-10-19 23:17:07689 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33690 }
[email protected]cf442612011-08-09 20:20:12691
692 // All the components that are not mentioned in the manifest we
693 // consider them up to date.
694 ChangeItemStatus(CrxUpdateItem::kChecking, CrxUpdateItem::kUpToDate);
695
[email protected]e8f96ff2011-08-03 05:07:33696 // If there are updates pending we do a short wait.
[email protected]a1f8c162012-09-27 22:32:21697 ScheduleNextRun(update_pending > 0);
[email protected]e8f96ff2011-08-03 05:07:33698}
699
700void CrxUpdateService::OnParseUpdateManifestFailed(
701 const std::string& error_message) {
702 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
703 size_t count = ChangeItemStatus(CrxUpdateItem::kChecking,
704 CrxUpdateItem::kNoUpdate);
[email protected]360b8bb2011-09-01 21:48:06705 config_->OnEvent(Configurator::kManifestError, static_cast<int>(count));
[email protected]e8f96ff2011-08-03 05:07:33706 DCHECK_GT(count, 0ul);
707 ScheduleNextRun(false);
708}
709
710// Called when the CRX package has been downloaded to a temporary location.
711// Here we fire the notifications and schedule the component-specific installer
712// to be called in the file thread.
[email protected]10c2d692012-05-11 05:32:23713void CrxUpdateService::OnURLFetchComplete(const net::URLFetcher* source,
[email protected]e8f96ff2011-08-03 05:07:33714 CRXContext* context) {
715 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
716 base::PlatformFileError error_code;
717
718 if (source->FileErrorOccurred(&error_code) || !FetchSuccess(*source)) {
719 size_t count = ChangeItemStatus(CrxUpdateItem::kDownloading,
720 CrxUpdateItem::kNoUpdate);
721 DCHECK_EQ(count, 1ul);
[email protected]360b8bb2011-09-01 21:48:06722 config_->OnEvent(Configurator::kNetworkError, CrxIdtoUMAId(context->id));
[email protected]cf442612011-08-09 20:20:12723 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33724 ScheduleNextRun(false);
725 } else {
[email protected]650b2d52013-02-10 03:41:45726 base::FilePath temp_crx_path;
[email protected]e8f96ff2011-08-03 05:07:33727 CHECK(source->GetResponseAsFilePath(true, &temp_crx_path));
728 size_t count = ChangeItemStatus(CrxUpdateItem::kDownloading,
729 CrxUpdateItem::kUpdating);
730 DCHECK_EQ(count, 1ul);
[email protected]cf442612011-08-09 20:20:12731 url_fetcher_.reset();
732
[email protected]ad50def52011-10-19 23:17:07733 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33734 chrome::NOTIFICATION_COMPONENT_UPDATE_READY,
[email protected]6c2381d2011-10-19 02:52:53735 content::Source<std::string>(&context->id),
[email protected]ad50def52011-10-19 23:17:07736 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33737
[email protected]44da56e2011-11-21 19:59:14738 // Why unretained? See comment at top of file.
[email protected]73251e72012-03-04 02:10:33739 BrowserThread::PostDelayedTask(
740 BrowserThread::FILE,
741 FROM_HERE,
[email protected]44da56e2011-11-21 19:59:14742 base::Bind(&CrxUpdateService::Install,
743 base::Unretained(this),
744 context,
745 temp_crx_path),
[email protected]73251e72012-03-04 02:10:33746 base::TimeDelta::FromMilliseconds(config_->StepDelay()));
[email protected]e8f96ff2011-08-03 05:07:33747 }
[email protected]e8f96ff2011-08-03 05:07:33748}
749
750// Install consists of digital signature verification, unpacking and then
751// calling the component specific installer. All that is handled by the
752// |unpacker|. If there is an error this function is in charge of deleting
753// the files created.
754void CrxUpdateService::Install(const CRXContext* context,
[email protected]650b2d52013-02-10 03:41:45755 const base::FilePath& crx_path) {
[email protected]e8f96ff2011-08-03 05:07:33756 // This function owns the |crx_path| and the |context| object.
757 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
758 ComponentUnpacker
759 unpacker(context->pk_hash, crx_path, context->installer);
[email protected]e8f96ff2011-08-03 05:07:33760 if (!file_util::Delete(crx_path, false)) {
761 NOTREACHED() << crx_path.value();
762 }
[email protected]44da56e2011-11-21 19:59:14763 // Why unretained? See comment at top of file.
[email protected]73251e72012-03-04 02:10:33764 BrowserThread::PostDelayedTask(
765 BrowserThread::UI,
766 FROM_HERE,
[email protected]44da56e2011-11-21 19:59:14767 base::Bind(&CrxUpdateService::DoneInstalling, base::Unretained(this),
768 context->id, unpacker.error()),
[email protected]73251e72012-03-04 02:10:33769 base::TimeDelta::FromMilliseconds(config_->StepDelay()));
[email protected]07f93af12011-08-17 20:57:22770 delete context;
[email protected]e8f96ff2011-08-03 05:07:33771}
772
773// Installation has been completed. Adjust the component status and
774// schedule the next check.
[email protected]07f93af12011-08-17 20:57:22775void CrxUpdateService::DoneInstalling(const std::string& component_id,
776 ComponentUnpacker::Error error) {
[email protected]e8f96ff2011-08-03 05:07:33777 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]07f93af12011-08-17 20:57:22778
779 CrxUpdateItem* item = FindUpdateItemById(component_id);
780 item->status = (error == ComponentUnpacker::kNone) ? CrxUpdateItem::kUpdated :
781 CrxUpdateItem::kNoUpdate;
782 if (item->status == CrxUpdateItem::kUpdated)
783 item->component.version = item->next_version;
784
[email protected]360b8bb2011-09-01 21:48:06785 Configurator::Events event;
786 switch (error) {
787 case ComponentUnpacker::kNone:
788 event = Configurator::kComponentUpdated;
789 break;
790 case ComponentUnpacker::kInstallerError:
791 event = Configurator::kInstallerError;
792 break;
793 default:
794 event = Configurator::kUnpackError;
795 break;
796 }
797
798 config_->OnEvent(event, CrxIdtoUMAId(component_id));
[email protected]e8f96ff2011-08-03 05:07:33799 ScheduleNextRun(false);
800}
801
802// The component update factory. Using the component updater as a singleton
803// is the job of the browser process.
804ComponentUpdateService* ComponentUpdateServiceFactory(
805 ComponentUpdateService::Configurator* config) {
806 DCHECK(config);
807 return new CrxUpdateService(config);
808}