blob: 44ae6409f1e5f69a5145f654caf0fd6cf53c862e [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>
[email protected]ccb4feef2013-02-14 06:16:478#include <set>
[email protected]e8f96ff2011-08-03 05:07:339#include <vector>
10
11#include "base/at_exit.h"
[email protected]44da56e2011-11-21 19:59:1412#include "base/bind.h"
[email protected]e8f96ff2011-08-03 05:07:3313#include "base/file_util.h"
[email protected]57999812013-02-24 05:40:5214#include "base/files/file_path.h"
[email protected]e8f96ff2011-08-03 05:07:3315#include "base/logging.h"
[email protected]7226b33c2011-08-18 08:44:2216#include "base/memory/scoped_ptr.h"
[email protected]e8f96ff2011-08-03 05:07:3317#include "base/stl_util.h"
[email protected]eb72b272011-12-19 16:10:5518#include "base/string_piece.h"
[email protected]e8f96ff2011-08-03 05:07:3319#include "base/string_util.h"
20#include "base/stringprintf.h"
[email protected]3ea1b182013-02-08 22:38:4121#include "base/strings/string_number_conversions.h"
[email protected]e8f96ff2011-08-03 05:07:3322#include "base/timer.h"
23#include "chrome/browser/browser_process.h"
24#include "chrome/browser/component_updater/component_unpacker.h"
25#include "chrome/common/chrome_notification_types.h"
26#include "chrome/common/chrome_utility_messages.h"
27#include "chrome/common/chrome_version_info.h"
28#include "chrome/common/extensions/extension.h"
[email protected]b7b63872013-01-03 02:41:1929#include "content/public/browser/browser_thread.h"
[email protected]ad50def52011-10-19 23:17:0730#include "content/public/browser/notification_service.h"
[email protected]c4f883a2012-02-03 17:02:0731#include "content/public/browser/utility_process_host.h"
32#include "content/public/browser/utility_process_host_client.h"
[email protected]e8f96ff2011-08-03 05:07:3333#include "googleurl/src/gurl.h"
34#include "net/base/escape.h"
35#include "net/base/load_flags.h"
[email protected]3dc1bc42012-06-19 08:20:5336#include "net/url_request/url_fetcher.h"
[email protected]15fb2aa2012-05-22 22:52:5937#include "net/url_request/url_fetcher_delegate.h"
[email protected]9d8ea302012-09-25 15:04:2238#include "net/url_request/url_request_status.h"
[email protected]e8f96ff2011-08-03 05:07:3339
[email protected]631bb742011-11-02 11:29:3940using content::BrowserThread;
[email protected]c4f883a2012-02-03 17:02:0741using content::UtilityProcessHost;
42using content::UtilityProcessHostClient;
[email protected]1c321ee52012-05-21 03:02:3443using extensions::Extension;
[email protected]631bb742011-11-02 11:29:3944
[email protected]44da56e2011-11-21 19:59:1445// The component updater is designed to live until process shutdown, so
46// base::Bind() calls are not refcounted.
47
[email protected]e8f96ff2011-08-03 05:07:3348namespace {
[email protected]dc06f0b2013-01-23 20:03:1649// Manifest sources, from most important to least important.
50const CrxComponent::UrlSource kManifestSources[] = {
51 CrxComponent::BANDAID,
52 CrxComponent::CWS_PUBLIC,
53 CrxComponent::CWS_SANDBOX
54};
55
[email protected]e8f96ff2011-08-03 05:07:3356// Extends an omaha compatible update check url |query| string. Does
57// not mutate the string if it would be longer than |limit| chars.
[email protected]4c37b452011-12-21 01:33:5258bool AddQueryString(const std::string& id,
59 const std::string& version,
60 size_t limit,
61 std::string* query) {
[email protected]e8f96ff2011-08-03 05:07:3362 std::string additional =
63 base::StringPrintf("id=%s&v=%s&uc", id.c_str(), version.c_str());
[email protected]4a19be92011-09-22 14:25:0264 additional = "x=" + net::EscapeQueryParamValue(additional, true);
[email protected]e8f96ff2011-08-03 05:07:3365 if ((additional.size() + query->size() + 1) > limit)
66 return false;
[email protected]926d36332011-10-05 01:06:2567 if (!query->empty())
68 query->append(1, '&');
[email protected]e8f96ff2011-08-03 05:07:3369 query->append(additional);
70 return true;
71}
72
[email protected]926d36332011-10-05 01:06:2573// Create the final omaha compatible query. The |extra| is optional and can
74// be null. It should contain top level (non-escaped) parameters.
75std::string MakeFinalQuery(const std::string& host,
76 const std::string& query,
77 const char* extra) {
78 std::string request(host);
79 request.append(1, '?');
80 if (extra) {
81 request.append(extra);
82 request.append(1, '&');
83 }
84 request.append(query);
85 return request;
86}
87
[email protected]e8f96ff2011-08-03 05:07:3388// Produces an extension-like friendly |id|. This might be removed in the
89// future if we roll our on packing tools.
90static std::string HexStringToID(const std::string& hexstr) {
91 std::string id;
92 for (size_t i = 0; i < hexstr.size(); ++i) {
93 int val;
[email protected]eb72b272011-12-19 16:10:5594 if (base::HexStringToInt(base::StringPiece(hexstr.begin() + i,
95 hexstr.begin() + i + 1),
96 &val)) {
[email protected]e8f96ff2011-08-03 05:07:3397 id.append(1, val + 'a');
[email protected]eb72b272011-12-19 16:10:5598 } else {
[email protected]e8f96ff2011-08-03 05:07:3399 id.append(1, 'a');
[email protected]eb72b272011-12-19 16:10:55100 }
[email protected]e8f96ff2011-08-03 05:07:33101 }
102 DCHECK(Extension::IdIsValid(id));
103 return id;
104}
105
[email protected]360b8bb2011-09-01 21:48:06106// Returns given a crx id it returns a small number, less than 100, that has a
107// decent chance of being unique among the registered components. It also has
108// the nice property that can be trivially computed by hand.
109static int CrxIdtoUMAId(const std::string& id) {
[email protected]a1f8c162012-09-27 22:32:21110 CHECK_GT(id.size(), 2U);
[email protected]360b8bb2011-09-01 21:48:06111 return id[0] + id[1] + id[2] - ('a' * 3);
112}
113
[email protected]e8f96ff2011-08-03 05:07:33114// Helper to do version check for components.
115bool IsVersionNewer(const Version& current, const std::string& proposed) {
116 Version proposed_ver(proposed);
117 if (!proposed_ver.IsValid())
118 return false;
119 return (current.CompareTo(proposed_ver) < 0);
120}
121
122// Helper template class that allows our main class to have separate
[email protected]f1050432012-02-15 01:35:46123// OnURLFetchComplete() callbacks for different types of url requests
[email protected]e8f96ff2011-08-03 05:07:33124// they are differentiated by the |Ctx| type.
125template <typename Del, typename Ctx>
[email protected]15fb2aa2012-05-22 22:52:59126class DelegateWithContext : public net::URLFetcherDelegate {
[email protected]e8f96ff2011-08-03 05:07:33127 public:
128 DelegateWithContext(Del* delegate, Ctx* context)
129 : delegate_(delegate), context_(context) {}
130
[email protected]10c2d692012-05-11 05:32:23131 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
[email protected]e8f96ff2011-08-03 05:07:33132 delegate_->OnURLFetchComplete(source, context_);
133 delete this;
134 }
135
136 private:
137 ~DelegateWithContext() {}
138
139 Del* delegate_;
140 Ctx* context_;
141};
142// This function creates the right DelegateWithContext using template inference.
143template <typename Del, typename Ctx>
[email protected]15fb2aa2012-05-22 22:52:59144net::URLFetcherDelegate* MakeContextDelegate(Del* delegate, Ctx* context) {
[email protected]e8f96ff2011-08-03 05:07:33145 return new DelegateWithContext<Del, Ctx>(delegate, context);
146}
147
148// Helper to start a url request using |fetcher| with the common flags.
[email protected]d3ec669b2012-05-23 07:12:14149void StartFetch(net::URLFetcher* fetcher,
[email protected]e8f96ff2011-08-03 05:07:33150 net::URLRequestContextGetter* context_getter,
151 bool save_to_file) {
[email protected]7cc6e5632011-10-25 17:56:12152 fetcher->SetRequestContext(context_getter);
153 fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
154 net::LOAD_DO_NOT_SAVE_COOKIES |
155 net::LOAD_DISABLE_CACHE);
[email protected]e8f96ff2011-08-03 05:07:33156 // TODO(cpu): Define our retry and backoff policy.
[email protected]7cc6e5632011-10-25 17:56:12157 fetcher->SetAutomaticallyRetryOn5xx(false);
[email protected]e8f96ff2011-08-03 05:07:33158 if (save_to_file) {
159 fetcher->SaveResponseToTemporaryFile(
160 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
161 }
162 fetcher->Start();
163}
164
165// Returs true if the url request of |fetcher| was succesful.
[email protected]10c2d692012-05-11 05:32:23166bool FetchSuccess(const net::URLFetcher& fetcher) {
[email protected]7cc6e5632011-10-25 17:56:12167 return (fetcher.GetStatus().status() == net::URLRequestStatus::SUCCESS) &&
168 (fetcher.GetResponseCode() == 200);
[email protected]e8f96ff2011-08-03 05:07:33169}
170
171// This is the one and only per-item state structure. Designed to be hosted
172// in a std::vector or a std::list. The two main members are |component|
173// which is supplied by the the component updater client and |status| which
174// is modified as the item is processed by the update pipeline. The expected
175// transition graph is:
176// error error error
177// +--kNoUpdate<------<-------+------<------+------<------+
178// | | | |
179// V yes | | |
180// kNew --->kChecking-->[update?]----->kCanUpdate-->kDownloading-->kUpdating
181// ^ | |
182// | |no |
183// |--kUpToDate<---+ |
184// | success |
185// +--kUpdated<-------------------------------------------+
186//
187struct CrxUpdateItem {
188 enum Status {
189 kNew,
190 kChecking,
191 kCanUpdate,
192 kDownloading,
193 kUpdating,
194 kUpdated,
195 kUpToDate,
196 kNoUpdate,
197 kLastStatus
198 };
199
200 Status status;
201 GURL crx_url;
202 std::string id;
203 base::Time last_check;
204 CrxComponent component;
[email protected]07f93af12011-08-17 20:57:22205 Version next_version;
[email protected]e8f96ff2011-08-03 05:07:33206
207 CrxUpdateItem() : status(kNew) {}
208
209 // Function object used to find a specific component.
210 class FindById {
211 public:
212 explicit FindById(const std::string& id) : id_(id) {}
213
214 bool operator() (CrxUpdateItem* item) const {
215 return (item->id == id_);
216 }
217 private:
218 const std::string& id_;
219 };
220};
221
222} // namespace.
223
224typedef ComponentUpdateService::Configurator Config;
225
[email protected]dc06f0b2013-01-23 20:03:16226CrxComponent::CrxComponent()
227 : installer(NULL),
228 source(BANDAID) {
229}
230
231CrxComponent::~CrxComponent() {
232}
[email protected]e8f96ff2011-08-03 05:07:33233
234//////////////////////////////////////////////////////////////////////////////
235// The one and only implementation of the ComponentUpdateService interface. In
236// charge of running the show. The main method is ProcessPendingItems() which
[email protected]50b01392012-09-01 03:33:13237// is called periodically to do the upgrades/installs or the update checks.
[email protected]e8f96ff2011-08-03 05:07:33238// An important consideration here is to be as "low impact" as we can to the
239// rest of the browser, so even if we have many components registered and
[email protected]f1050432012-02-15 01:35:46240// eligible for update, we only do one thing at a time with pauses in between
[email protected]e8f96ff2011-08-03 05:07:33241// the tasks. Also when we do network requests there is only one |url_fetcher_|
242// in flight at at a time.
243// There are no locks in this code, the main structure |work_items_| is mutated
244// only from the UI thread. The unpack and installation is done in the file
245// thread and the network requests are done in the IO thread and in the file
246// thread.
247class CrxUpdateService : public ComponentUpdateService {
248 public:
249 explicit CrxUpdateService(ComponentUpdateService::Configurator* config);
250
251 virtual ~CrxUpdateService();
252
253 // Overrides for ComponentUpdateService.
254 virtual Status Start() OVERRIDE;
255 virtual Status Stop() OVERRIDE;
256 virtual Status RegisterComponent(const CrxComponent& component) OVERRIDE;
[email protected]ccb4feef2013-02-14 06:16:47257 virtual Status CheckForUpdateSoon(const CrxComponent& component) OVERRIDE;
[email protected]e8f96ff2011-08-03 05:07:33258
259 // The only purpose of this class is to forward the
[email protected]c4f883a2012-02-03 17:02:07260 // UtilityProcessHostClient callbacks so CrxUpdateService does
[email protected]e8f96ff2011-08-03 05:07:33261 // not have to derive from it because that is refcounted.
[email protected]c4f883a2012-02-03 17:02:07262 class ManifestParserBridge : public UtilityProcessHostClient {
[email protected]e8f96ff2011-08-03 05:07:33263 public:
264 explicit ManifestParserBridge(CrxUpdateService* service)
265 : service_(service) {}
266
267 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
268 bool handled = true;
269 IPC_BEGIN_MESSAGE_MAP(ManifestParserBridge, message)
[email protected]2ccf45c2011-08-19 23:35:50270 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseUpdateManifest_Succeeded,
[email protected]e8f96ff2011-08-03 05:07:33271 OnParseUpdateManifestSucceeded)
[email protected]2ccf45c2011-08-19 23:35:50272 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseUpdateManifest_Failed,
[email protected]e8f96ff2011-08-03 05:07:33273 OnParseUpdateManifestFailed)
274 IPC_MESSAGE_UNHANDLED(handled = false)
275 IPC_END_MESSAGE_MAP()
276 return handled;
277 }
278
279 private:
[email protected]a3988cc2012-04-27 05:07:18280 virtual ~ManifestParserBridge() {}
281
[email protected]f1050432012-02-15 01:35:46282 // Omaha update response XML was successfully parsed.
[email protected]e8f96ff2011-08-03 05:07:33283 void OnParseUpdateManifestSucceeded(const UpdateManifest::Results& r) {
284 service_->OnParseUpdateManifestSucceeded(r);
285 }
286 // Omaha update response XML could not be parsed.
287 void OnParseUpdateManifestFailed(const std::string& e) {
288 service_->OnParseUpdateManifestFailed(e);
289 }
290
291 CrxUpdateService* service_;
292 DISALLOW_COPY_AND_ASSIGN(ManifestParserBridge);
293 };
294
295 // Context for a update check url request. See DelegateWithContext above.
296 struct UpdateContext {
297 base::Time start;
298 UpdateContext() : start(base::Time::Now()) {}
299 };
300
301 // Context for a crx download url request. See DelegateWithContext above.
302 struct CRXContext {
303 ComponentInstaller* installer;
304 std::vector<uint8> pk_hash;
305 std::string id;
306 CRXContext() : installer(NULL) {}
307 };
308
[email protected]10c2d692012-05-11 05:32:23309 void OnURLFetchComplete(const net::URLFetcher* source,
[email protected]7cc6e5632011-10-25 17:56:12310 UpdateContext* context);
[email protected]e8f96ff2011-08-03 05:07:33311
[email protected]10c2d692012-05-11 05:32:23312 void OnURLFetchComplete(const net::URLFetcher* source,
[email protected]7cc6e5632011-10-25 17:56:12313 CRXContext* context);
[email protected]e8f96ff2011-08-03 05:07:33314
315 private:
316 // See ManifestParserBridge.
317 void OnParseUpdateManifestSucceeded(
318 const UpdateManifest::Results& results);
319
320 // See ManifestParserBridge.
321 void OnParseUpdateManifestFailed(
322 const std::string& error_message);
323
324 bool AddItemToUpdateCheck(CrxUpdateItem* item, std::string* query);
325
326 void ProcessPendingItems();
327
328 void ScheduleNextRun(bool step_delay);
329
330 void ParseManifest(const std::string& xml);
331
[email protected]650b2d52013-02-10 03:41:45332 void Install(const CRXContext* context, const base::FilePath& crx_path);
[email protected]e8f96ff2011-08-03 05:07:33333
[email protected]07f93af12011-08-17 20:57:22334 void DoneInstalling(const std::string& component_id,
335 ComponentUnpacker::Error error);
[email protected]e8f96ff2011-08-03 05:07:33336
337 size_t ChangeItemStatus(CrxUpdateItem::Status from,
338 CrxUpdateItem::Status to);
339
340 CrxUpdateItem* FindUpdateItemById(const std::string& id);
341
342 scoped_ptr<Config> config_;
343
[email protected]d3ec669b2012-05-23 07:12:14344 scoped_ptr<net::URLFetcher> url_fetcher_;
[email protected]e8f96ff2011-08-03 05:07:33345
346 typedef std::vector<CrxUpdateItem*> UpdateItems;
[email protected]ccb4feef2013-02-14 06:16:47347 // A collection of every work item.
[email protected]e8f96ff2011-08-03 05:07:33348 UpdateItems work_items_;
349
[email protected]ccb4feef2013-02-14 06:16:47350 // A particular set of items from work_items_, which should be checked ASAP.
351 std::set<CrxUpdateItem*> requested_work_items_;
352
[email protected]e8f96ff2011-08-03 05:07:33353 base::OneShotTimer<CrxUpdateService> timer_;
354
355 Version chrome_version_;
356
357 bool running_;
358
359 DISALLOW_COPY_AND_ASSIGN(CrxUpdateService);
360};
361
[email protected]e8f96ff2011-08-03 05:07:33362//////////////////////////////////////////////////////////////////////////////
363
364CrxUpdateService::CrxUpdateService(
365 ComponentUpdateService::Configurator* config)
366 : config_(config),
367 chrome_version_(chrome::VersionInfo().Version()),
368 running_(false) {
369}
370
371CrxUpdateService::~CrxUpdateService() {
372 // Because we are a singleton, at this point only the UI thread should be
373 // alive, this simplifies the management of the work that could be in
374 // flight in other threads.
375 Stop();
376 STLDeleteElements(&work_items_);
377}
378
379ComponentUpdateService::Status CrxUpdateService::Start() {
380 // Note that RegisterComponent will call Start() when the first
381 // component is registered, so it can be called twice. This way
382 // we avoid scheduling the timer if there is no work to do.
383 running_ = true;
384 if (work_items_.empty())
385 return kOk;
386
[email protected]ad50def52011-10-19 23:17:07387 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33388 chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED,
[email protected]6c2381d2011-10-19 02:52:53389 content::Source<ComponentUpdateService>(this),
[email protected]ad50def52011-10-19 23:17:07390 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33391
[email protected]d323a172011-09-02 18:23:02392 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(config_->InitialDelay()),
[email protected]e8f96ff2011-08-03 05:07:33393 this, &CrxUpdateService::ProcessPendingItems);
394 return kOk;
395}
396
397// Stop the main check + update loop. In flight operations will be
398// completed.
399ComponentUpdateService::Status CrxUpdateService::Stop() {
400 running_ = false;
401 timer_.Stop();
402 return kOk;
403}
404
[email protected]ccb4feef2013-02-14 06:16:47405// This function sets the timer which will call ProcessPendingItems() or
406// ProcessRequestedItem() if there is an important requested item. There
407// are two kinds of waits: a short step_delay (when step_status is
408// kPrevInProgress) and a long one when a full check/update cycle
409// has completed either successfully or with an error.
[email protected]e8f96ff2011-08-03 05:07:33410void CrxUpdateService::ScheduleNextRun(bool step_delay) {
411 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]cf442612011-08-09 20:20:12412 DCHECK(url_fetcher_.get() == NULL);
[email protected]e8f96ff2011-08-03 05:07:33413 CHECK(!timer_.IsRunning());
414 // It could be the case that Stop() had been called while a url request
415 // or unpacking was in flight, if so we arrive here but |running_| is
416 // false. In that case do not loop again.
417 if (!running_)
418 return;
419
[email protected]ccb4feef2013-02-14 06:16:47420 // Keep the delay short if in the middle of an update (step_delay),
421 // or there are new requested_work_items_ that have not been processed yet.
422 int64 delay = (step_delay || requested_work_items_.size() > 0)
423 ? config_->StepDelay() : config_->NextCheckDelay();
[email protected]cf442612011-08-09 20:20:12424
[email protected]e8f96ff2011-08-03 05:07:33425 if (!step_delay) {
[email protected]ad50def52011-10-19 23:17:07426 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33427 chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING,
[email protected]6c2381d2011-10-19 02:52:53428 content::Source<ComponentUpdateService>(this),
[email protected]ad50def52011-10-19 23:17:07429 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33430 // Zero is only used for unit tests.
[email protected]cf442612011-08-09 20:20:12431 if (0 == delay)
[email protected]e8f96ff2011-08-03 05:07:33432 return;
433 }
434
[email protected]d323a172011-09-02 18:23:02435 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(delay),
[email protected]e8f96ff2011-08-03 05:07:33436 this, &CrxUpdateService::ProcessPendingItems);
437}
438
439// Given a extension-like component id, find the associated component.
440CrxUpdateItem* CrxUpdateService::FindUpdateItemById(const std::string& id) {
441 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
442 CrxUpdateItem::FindById finder(id);
443 UpdateItems::iterator it = std::find_if(work_items_.begin(),
444 work_items_.end(),
445 finder);
446 if (it == work_items_.end())
447 return NULL;
448 return (*it);
449}
450
451// Changes all the components in |work_items_| that have |from| status to
452// |to| statatus and returns how many have been changed.
453size_t CrxUpdateService::ChangeItemStatus(CrxUpdateItem::Status from,
454 CrxUpdateItem::Status to) {
455 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
456 size_t count = 0;
457 for (UpdateItems::iterator it = work_items_.begin();
458 it != work_items_.end(); ++it) {
459 CrxUpdateItem* item = *it;
460 if (item->status != from)
461 continue;
462 item->status = to;
463 ++count;
464 }
465 return count;
466}
467
468// Adds a component to be checked for upgrades. If the component exists it
469// it will be replaced and the return code is kReplaced.
470//
471// TODO(cpu): Evaluate if we want to support un-registration.
472ComponentUpdateService::Status CrxUpdateService::RegisterComponent(
473 const CrxComponent& component) {
474 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
475 if (component.pk_hash.empty() ||
476 !component.version.IsValid() ||
477 !component.installer)
478 return kError;
479
480 std::string id =
481 HexStringToID(StringToLowerASCII(base::HexEncode(&component.pk_hash[0],
482 component.pk_hash.size()/2)));
483 CrxUpdateItem* uit;
484 uit = FindUpdateItemById(id);
485 if (uit) {
486 uit->component = component;
487 return kReplaced;
488 }
489
490 uit = new CrxUpdateItem;
491 uit->id.swap(id);
492 uit->component = component;
493 work_items_.push_back(uit);
494 // If this is the first component registered we call Start to
495 // schedule the first timer.
496 if (running_ && (work_items_.size() == 1))
497 Start();
498
499 return kOk;
500}
501
502// Sets a component to be checked for updates.
503// The componet to add is |crxit| and the |query| string is modified with the
504// required omaha compatible query. Returns false when the query strings
505// is longer than specified by UrlSizeLimit().
506bool CrxUpdateService::AddItemToUpdateCheck(CrxUpdateItem* item,
507 std::string* query) {
508 if (!AddQueryString(item->id,
509 item->component.version.GetString(),
510 config_->UrlSizeLimit(), query))
511 return false;
512 item->status = CrxUpdateItem::kChecking;
513 item->last_check = base::Time::Now();
514 return true;
515}
516
[email protected]ccb4feef2013-02-14 06:16:47517// Start the process of checking for an update, for a particular component
518// that was previously registered.
519ComponentUpdateService::Status CrxUpdateService::CheckForUpdateSoon(
520 const CrxComponent& component) {
521 if (component.pk_hash.empty() ||
522 !component.version.IsValid() ||
523 !component.installer)
524 return kError;
525
526 std::string id =
527 HexStringToID(StringToLowerASCII(base::HexEncode(&component.pk_hash[0],
528 component.pk_hash.size()/2)));
529
530 CrxUpdateItem* uit;
531 uit = FindUpdateItemById(id);
532 if (!uit)
533 return kError;
534
535 // Check if the request is too soon.
536 base::TimeDelta delta = base::Time::Now() - uit->last_check;
537 if (delta < base::TimeDelta::FromSeconds(config_->OnDemandDelay())) {
538 return kError;
539 }
540
541 switch (uit->status) {
542 // If the item is already in the process of being updated, there is
543 // no point in this call, so return kInProgress.
544 case CrxUpdateItem::kChecking:
545 case CrxUpdateItem::kCanUpdate:
546 case CrxUpdateItem::kDownloading:
547 case CrxUpdateItem::kUpdating:
548 return kInProgress;
549 // Otherwise the item was already checked a while back (or it is new),
550 // set its status to kNew to give it a slightly higher priority.
551 case CrxUpdateItem::kNew:
552 case CrxUpdateItem::kUpdated:
553 case CrxUpdateItem::kUpToDate:
554 case CrxUpdateItem::kNoUpdate:
555 uit->status = CrxUpdateItem::kNew;
556 requested_work_items_.insert(uit);
557 break;
558 case CrxUpdateItem::kLastStatus:
559 NOTREACHED() << uit->status;
560 }
561
562 // In case the current delay is long, set the timer to a shorter value
563 // to get the ball rolling.
564 if (timer_.IsRunning()) {
565 timer_.Stop();
566 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(config_->StepDelay()),
567 this, &CrxUpdateService::ProcessPendingItems);
568 }
569
570 return kOk;
571}
572
[email protected]e8f96ff2011-08-03 05:07:33573// Here is where the work gets scheduled. Given that our |work_items_| list
574// is expected to be ten or less items, we simply loop several times.
575void CrxUpdateService::ProcessPendingItems() {
576 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
577 // First check for ready upgrades and do one. The first
578 // step is to fetch the crx package.
579 for (UpdateItems::const_iterator it = work_items_.begin();
580 it != work_items_.end(); ++it) {
581 CrxUpdateItem* item = *it;
582 if (item->status != CrxUpdateItem::kCanUpdate)
583 continue;
584 // Found component to update, start the process.
585 item->status = CrxUpdateItem::kDownloading;
586 CRXContext* context = new CRXContext;
587 context->pk_hash = item->component.pk_hash;
588 context->id = item->id;
589 context->installer = item->component.installer;
[email protected]3dc1bc42012-06-19 08:20:53590 url_fetcher_.reset(net::URLFetcher::Create(
[email protected]d3ec669b2012-05-23 07:12:14591 0, item->crx_url, net::URLFetcher::GET,
[email protected]e8f96ff2011-08-03 05:07:33592 MakeContextDelegate(this, context)));
593 StartFetch(url_fetcher_.get(), config_->RequestContext(), true);
594 return;
595 }
596
[email protected]dc06f0b2013-01-23 20:03:16597 for (size_t ix = 0; ix != arraysize(kManifestSources); ++ix) {
598 const CrxComponent::UrlSource manifest_source = kManifestSources[ix];
[email protected]e8f96ff2011-08-03 05:07:33599
[email protected]dc06f0b2013-01-23 20:03:16600 std::string query;
601 // If no pending upgrades, we check if there are new components we have not
602 // checked against the server. We can batch some in a single url request.
603 for (UpdateItems::const_iterator it = work_items_.begin();
604 it != work_items_.end(); ++it) {
605 CrxUpdateItem* item = *it;
606 if (item->status != CrxUpdateItem::kNew)
607 continue;
608 if (item->component.source != manifest_source)
609 continue;
610 if (!AddItemToUpdateCheck(item, &query))
611 break;
[email protected]ccb4feef2013-02-14 06:16:47612 // Requested work items may speed up the update cycle up until
613 // the point that we start an update check. I.e., transition
614 // from kNew -> kChecking. Since the service doesn't guarantee that
615 // the requested items make it any further than kChecking,
616 // forget them now.
617 requested_work_items_.erase(item);
[email protected]dc06f0b2013-01-23 20:03:16618 }
[email protected]e8f96ff2011-08-03 05:07:33619
[email protected]dc06f0b2013-01-23 20:03:16620 // Next we can go back to components we already checked, here
621 // we can also batch them in a single url request, as long as
622 // we have not checked them recently.
623 const base::TimeDelta min_delta_time =
624 base::TimeDelta::FromSeconds(config_->MinimumReCheckWait());
[email protected]e8f96ff2011-08-03 05:07:33625
[email protected]dc06f0b2013-01-23 20:03:16626 for (UpdateItems::const_iterator it = work_items_.begin();
627 it != work_items_.end(); ++it) {
628 CrxUpdateItem* item = *it;
629 if ((item->status != CrxUpdateItem::kNoUpdate) &&
630 (item->status != CrxUpdateItem::kUpToDate))
631 continue;
632 if (item->component.source != manifest_source)
633 continue;
634 base::TimeDelta delta = base::Time::Now() - item->last_check;
635 if (delta < min_delta_time)
636 continue;
637 if (!AddItemToUpdateCheck(item, &query))
638 break;
639 }
640
641 // Finally, we check components that we already updated as long as
642 // we have not checked them recently.
643 for (UpdateItems::const_iterator it = work_items_.begin();
644 it != work_items_.end(); ++it) {
645 CrxUpdateItem* item = *it;
646 if (item->status != CrxUpdateItem::kUpdated)
647 continue;
648 if (item->component.source != manifest_source)
649 continue;
650 base::TimeDelta delta = base::Time::Now() - item->last_check;
651 if (delta < min_delta_time)
652 continue;
653 if (!AddItemToUpdateCheck(item, &query))
654 break;
655 }
656
657 // If no components to update we move down to the next source.
658 if (query.empty())
659 continue;
660
661 // We got components to check. Start the url request and exit.
662 const std::string full_query =
663 MakeFinalQuery(config_->UpdateUrl(manifest_source).spec(),
664 query,
665 config_->ExtraRequestParams());
666
667 url_fetcher_.reset(net::URLFetcher::Create(
668 0, GURL(full_query), net::URLFetcher::GET,
669 MakeContextDelegate(this, new UpdateContext())));
670 StartFetch(url_fetcher_.get(), config_->RequestContext(), false);
[email protected]e8f96ff2011-08-03 05:07:33671 return;
672 }
673
[email protected]dc06f0b2013-01-23 20:03:16674 // No components to update. Next check after the long sleep.
675 ScheduleNextRun(false);
[email protected]e8f96ff2011-08-03 05:07:33676}
677
[email protected]ccb4feef2013-02-14 06:16:47678// Called when we got a response from the update server. It consists of an xml
[email protected]e8f96ff2011-08-03 05:07:33679// document following the omaha update scheme.
[email protected]10c2d692012-05-11 05:32:23680void CrxUpdateService::OnURLFetchComplete(const net::URLFetcher* source,
[email protected]07f93af12011-08-17 20:57:22681 UpdateContext* context) {
[email protected]e8f96ff2011-08-03 05:07:33682 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
683 if (FetchSuccess(*source)) {
684 std::string xml;
685 source->GetResponseAsString(&xml);
[email protected]cf442612011-08-09 20:20:12686 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33687 ParseManifest(xml);
688 } else {
[email protected]cf442612011-08-09 20:20:12689 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33690 CrxUpdateService::OnParseUpdateManifestFailed("network error");
691 }
[email protected]07f93af12011-08-17 20:57:22692 delete context;
[email protected]e8f96ff2011-08-03 05:07:33693}
694
695// Parsing the manifest is either done right now for tests or in a sandboxed
696// process for the production environment. This mitigates the case where an
697// attacker was able to feed us a malicious xml string.
698void CrxUpdateService::ParseManifest(const std::string& xml) {
699 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
700 if (config_->InProcess()) {
701 UpdateManifest manifest;
702 if (!manifest.Parse(xml)) {
703 CrxUpdateService::OnParseUpdateManifestFailed(manifest.errors());
704 } else {
705 CrxUpdateService::OnParseUpdateManifestSucceeded(manifest.results());
706 }
707 } else {
[email protected]c4f883a2012-02-03 17:02:07708 UtilityProcessHost* host = UtilityProcessHost::Create(
[email protected]7f8f24f2012-11-15 19:40:14709 new ManifestParserBridge(this),
710 base::MessageLoopProxy::current());
[email protected]c4f883a2012-02-03 17:02:07711 host->EnableZygote();
[email protected]2ccf45c2011-08-19 23:35:50712 host->Send(new ChromeUtilityMsg_ParseUpdateManifest(xml));
[email protected]e8f96ff2011-08-03 05:07:33713 }
714}
715
716// A valid Omaha update check has arrived, from only the list of components that
717// we are currently upgrading we check for a match in which the server side
718// version is newer, if so we queue them for an upgrade. The next time we call
719// ProcessPendingItems() one of them will be drafted for the upgrade process.
720void CrxUpdateService::OnParseUpdateManifestSucceeded(
721 const UpdateManifest::Results& results) {
722 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]a1f8c162012-09-27 22:32:21723 size_t update_pending = 0;
[email protected]e8f96ff2011-08-03 05:07:33724 std::vector<UpdateManifest::Result>::const_iterator it;
725 for (it = results.list.begin(); it != results.list.end(); ++it) {
726 CrxUpdateItem* crx = FindUpdateItemById(it->extension_id);
727 if (!crx)
728 continue;
729
730 if (crx->status != CrxUpdateItem::kChecking)
731 continue; // Not updating this component now.
732
[email protected]360b8bb2011-09-01 21:48:06733 config_->OnEvent(Configurator::kManifestCheck, CrxIdtoUMAId(crx->id));
734
[email protected]e8f96ff2011-08-03 05:07:33735 if (it->version.empty()) {
[email protected]cf442612011-08-09 20:20:12736 // No version means no update available.
[email protected]e8f96ff2011-08-03 05:07:33737 crx->status = CrxUpdateItem::kNoUpdate;
[email protected]cf442612011-08-09 20:20:12738 continue;
[email protected]e8f96ff2011-08-03 05:07:33739 }
740 if (!IsVersionNewer(crx->component.version, it->version)) {
[email protected]cf442612011-08-09 20:20:12741 // Our component is up to date.
[email protected]e8f96ff2011-08-03 05:07:33742 crx->status = CrxUpdateItem::kUpToDate;
[email protected]cf442612011-08-09 20:20:12743 continue;
[email protected]e8f96ff2011-08-03 05:07:33744 }
745 if (!it->browser_min_version.empty()) {
[email protected]cf442612011-08-09 20:20:12746 if (IsVersionNewer(chrome_version_, it->browser_min_version)) {
747 // Does not apply for this chrome version.
748 crx->status = CrxUpdateItem::kNoUpdate;
749 continue;
750 }
[email protected]e8f96ff2011-08-03 05:07:33751 }
752 // All test passed. Queue an upgrade for this component and fire the
753 // notifications.
754 crx->crx_url = it->crx_url;
755 crx->status = CrxUpdateItem::kCanUpdate;
[email protected]07f93af12011-08-17 20:57:22756 crx->next_version = Version(it->version);
[email protected]e8f96ff2011-08-03 05:07:33757 ++update_pending;
758
[email protected]ad50def52011-10-19 23:17:07759 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33760 chrome::NOTIFICATION_COMPONENT_UPDATE_FOUND,
[email protected]6c2381d2011-10-19 02:52:53761 content::Source<std::string>(&crx->id),
[email protected]ad50def52011-10-19 23:17:07762 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33763 }
[email protected]cf442612011-08-09 20:20:12764
765 // All the components that are not mentioned in the manifest we
766 // consider them up to date.
767 ChangeItemStatus(CrxUpdateItem::kChecking, CrxUpdateItem::kUpToDate);
768
[email protected]e8f96ff2011-08-03 05:07:33769 // If there are updates pending we do a short wait.
[email protected]a1f8c162012-09-27 22:32:21770 ScheduleNextRun(update_pending > 0);
[email protected]e8f96ff2011-08-03 05:07:33771}
772
773void CrxUpdateService::OnParseUpdateManifestFailed(
774 const std::string& error_message) {
775 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
776 size_t count = ChangeItemStatus(CrxUpdateItem::kChecking,
777 CrxUpdateItem::kNoUpdate);
[email protected]360b8bb2011-09-01 21:48:06778 config_->OnEvent(Configurator::kManifestError, static_cast<int>(count));
[email protected]e8f96ff2011-08-03 05:07:33779 DCHECK_GT(count, 0ul);
780 ScheduleNextRun(false);
781}
782
783// Called when the CRX package has been downloaded to a temporary location.
784// Here we fire the notifications and schedule the component-specific installer
785// to be called in the file thread.
[email protected]10c2d692012-05-11 05:32:23786void CrxUpdateService::OnURLFetchComplete(const net::URLFetcher* source,
[email protected]e8f96ff2011-08-03 05:07:33787 CRXContext* context) {
788 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
789 base::PlatformFileError error_code;
790
791 if (source->FileErrorOccurred(&error_code) || !FetchSuccess(*source)) {
792 size_t count = ChangeItemStatus(CrxUpdateItem::kDownloading,
793 CrxUpdateItem::kNoUpdate);
794 DCHECK_EQ(count, 1ul);
[email protected]360b8bb2011-09-01 21:48:06795 config_->OnEvent(Configurator::kNetworkError, CrxIdtoUMAId(context->id));
[email protected]cf442612011-08-09 20:20:12796 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33797 ScheduleNextRun(false);
798 } else {
[email protected]650b2d52013-02-10 03:41:45799 base::FilePath temp_crx_path;
[email protected]e8f96ff2011-08-03 05:07:33800 CHECK(source->GetResponseAsFilePath(true, &temp_crx_path));
801 size_t count = ChangeItemStatus(CrxUpdateItem::kDownloading,
802 CrxUpdateItem::kUpdating);
803 DCHECK_EQ(count, 1ul);
[email protected]cf442612011-08-09 20:20:12804 url_fetcher_.reset();
805
[email protected]ad50def52011-10-19 23:17:07806 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33807 chrome::NOTIFICATION_COMPONENT_UPDATE_READY,
[email protected]6c2381d2011-10-19 02:52:53808 content::Source<std::string>(&context->id),
[email protected]ad50def52011-10-19 23:17:07809 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33810
[email protected]44da56e2011-11-21 19:59:14811 // Why unretained? See comment at top of file.
[email protected]73251e72012-03-04 02:10:33812 BrowserThread::PostDelayedTask(
813 BrowserThread::FILE,
814 FROM_HERE,
[email protected]44da56e2011-11-21 19:59:14815 base::Bind(&CrxUpdateService::Install,
816 base::Unretained(this),
817 context,
818 temp_crx_path),
[email protected]73251e72012-03-04 02:10:33819 base::TimeDelta::FromMilliseconds(config_->StepDelay()));
[email protected]e8f96ff2011-08-03 05:07:33820 }
[email protected]e8f96ff2011-08-03 05:07:33821}
822
823// Install consists of digital signature verification, unpacking and then
824// calling the component specific installer. All that is handled by the
825// |unpacker|. If there is an error this function is in charge of deleting
826// the files created.
827void CrxUpdateService::Install(const CRXContext* context,
[email protected]650b2d52013-02-10 03:41:45828 const base::FilePath& crx_path) {
[email protected]e8f96ff2011-08-03 05:07:33829 // This function owns the |crx_path| and the |context| object.
830 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
831 ComponentUnpacker
832 unpacker(context->pk_hash, crx_path, context->installer);
[email protected]e8f96ff2011-08-03 05:07:33833 if (!file_util::Delete(crx_path, false)) {
834 NOTREACHED() << crx_path.value();
835 }
[email protected]44da56e2011-11-21 19:59:14836 // Why unretained? See comment at top of file.
[email protected]73251e72012-03-04 02:10:33837 BrowserThread::PostDelayedTask(
838 BrowserThread::UI,
839 FROM_HERE,
[email protected]44da56e2011-11-21 19:59:14840 base::Bind(&CrxUpdateService::DoneInstalling, base::Unretained(this),
841 context->id, unpacker.error()),
[email protected]73251e72012-03-04 02:10:33842 base::TimeDelta::FromMilliseconds(config_->StepDelay()));
[email protected]07f93af12011-08-17 20:57:22843 delete context;
[email protected]e8f96ff2011-08-03 05:07:33844}
845
846// Installation has been completed. Adjust the component status and
847// schedule the next check.
[email protected]07f93af12011-08-17 20:57:22848void CrxUpdateService::DoneInstalling(const std::string& component_id,
849 ComponentUnpacker::Error error) {
[email protected]e8f96ff2011-08-03 05:07:33850 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]07f93af12011-08-17 20:57:22851
852 CrxUpdateItem* item = FindUpdateItemById(component_id);
853 item->status = (error == ComponentUnpacker::kNone) ? CrxUpdateItem::kUpdated :
854 CrxUpdateItem::kNoUpdate;
855 if (item->status == CrxUpdateItem::kUpdated)
856 item->component.version = item->next_version;
857
[email protected]360b8bb2011-09-01 21:48:06858 Configurator::Events event;
859 switch (error) {
860 case ComponentUnpacker::kNone:
861 event = Configurator::kComponentUpdated;
862 break;
863 case ComponentUnpacker::kInstallerError:
864 event = Configurator::kInstallerError;
865 break;
866 default:
867 event = Configurator::kUnpackError;
868 break;
869 }
870
871 config_->OnEvent(event, CrxIdtoUMAId(component_id));
[email protected]e8f96ff2011-08-03 05:07:33872 ScheduleNextRun(false);
873}
874
875// The component update factory. Using the component updater as a singleton
876// is the job of the browser process.
877ComponentUpdateService* ComponentUpdateServiceFactory(
878 ComponentUpdateService::Configurator* config) {
879 DCHECK(config);
880 return new CrxUpdateService(config);
881}