blob: 9f0656ba06af444756a76e82605f0e4883ddc0ad [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"
17#include "base/string_number_conversions.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"
21#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 {
48// Extends an omaha compatible update check url |query| string. Does
49// not mutate the string if it would be longer than |limit| chars.
[email protected]4c37b452011-12-21 01:33:5250bool AddQueryString(const std::string& id,
51 const std::string& version,
52 size_t limit,
53 std::string* query) {
[email protected]e8f96ff2011-08-03 05:07:3354 std::string additional =
55 base::StringPrintf("id=%s&v=%s&uc", id.c_str(), version.c_str());
[email protected]4a19be92011-09-22 14:25:0256 additional = "x=" + net::EscapeQueryParamValue(additional, true);
[email protected]e8f96ff2011-08-03 05:07:3357 if ((additional.size() + query->size() + 1) > limit)
58 return false;
[email protected]926d36332011-10-05 01:06:2559 if (!query->empty())
60 query->append(1, '&');
[email protected]e8f96ff2011-08-03 05:07:3361 query->append(additional);
62 return true;
63}
64
[email protected]926d36332011-10-05 01:06:2565// Create the final omaha compatible query. The |extra| is optional and can
66// be null. It should contain top level (non-escaped) parameters.
67std::string MakeFinalQuery(const std::string& host,
68 const std::string& query,
69 const char* extra) {
70 std::string request(host);
71 request.append(1, '?');
72 if (extra) {
73 request.append(extra);
74 request.append(1, '&');
75 }
76 request.append(query);
77 return request;
78}
79
[email protected]e8f96ff2011-08-03 05:07:3380// Produces an extension-like friendly |id|. This might be removed in the
81// future if we roll our on packing tools.
82static std::string HexStringToID(const std::string& hexstr) {
83 std::string id;
84 for (size_t i = 0; i < hexstr.size(); ++i) {
85 int val;
[email protected]eb72b272011-12-19 16:10:5586 if (base::HexStringToInt(base::StringPiece(hexstr.begin() + i,
87 hexstr.begin() + i + 1),
88 &val)) {
[email protected]e8f96ff2011-08-03 05:07:3389 id.append(1, val + 'a');
[email protected]eb72b272011-12-19 16:10:5590 } else {
[email protected]e8f96ff2011-08-03 05:07:3391 id.append(1, 'a');
[email protected]eb72b272011-12-19 16:10:5592 }
[email protected]e8f96ff2011-08-03 05:07:3393 }
94 DCHECK(Extension::IdIsValid(id));
95 return id;
96}
97
[email protected]360b8bb2011-09-01 21:48:0698// Returns given a crx id it returns a small number, less than 100, that has a
99// decent chance of being unique among the registered components. It also has
100// the nice property that can be trivially computed by hand.
101static int CrxIdtoUMAId(const std::string& id) {
[email protected]a1f8c162012-09-27 22:32:21102 CHECK_GT(id.size(), 2U);
[email protected]360b8bb2011-09-01 21:48:06103 return id[0] + id[1] + id[2] - ('a' * 3);
104}
105
[email protected]e8f96ff2011-08-03 05:07:33106// Helper to do version check for components.
107bool IsVersionNewer(const Version& current, const std::string& proposed) {
108 Version proposed_ver(proposed);
109 if (!proposed_ver.IsValid())
110 return false;
111 return (current.CompareTo(proposed_ver) < 0);
112}
113
114// Helper template class that allows our main class to have separate
[email protected]f1050432012-02-15 01:35:46115// OnURLFetchComplete() callbacks for different types of url requests
[email protected]e8f96ff2011-08-03 05:07:33116// they are differentiated by the |Ctx| type.
117template <typename Del, typename Ctx>
[email protected]15fb2aa2012-05-22 22:52:59118class DelegateWithContext : public net::URLFetcherDelegate {
[email protected]e8f96ff2011-08-03 05:07:33119 public:
120 DelegateWithContext(Del* delegate, Ctx* context)
121 : delegate_(delegate), context_(context) {}
122
[email protected]10c2d692012-05-11 05:32:23123 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
[email protected]e8f96ff2011-08-03 05:07:33124 delegate_->OnURLFetchComplete(source, context_);
125 delete this;
126 }
127
128 private:
129 ~DelegateWithContext() {}
130
131 Del* delegate_;
132 Ctx* context_;
133};
134// This function creates the right DelegateWithContext using template inference.
135template <typename Del, typename Ctx>
[email protected]15fb2aa2012-05-22 22:52:59136net::URLFetcherDelegate* MakeContextDelegate(Del* delegate, Ctx* context) {
[email protected]e8f96ff2011-08-03 05:07:33137 return new DelegateWithContext<Del, Ctx>(delegate, context);
138}
139
140// Helper to start a url request using |fetcher| with the common flags.
[email protected]d3ec669b2012-05-23 07:12:14141void StartFetch(net::URLFetcher* fetcher,
[email protected]e8f96ff2011-08-03 05:07:33142 net::URLRequestContextGetter* context_getter,
143 bool save_to_file) {
[email protected]7cc6e5632011-10-25 17:56:12144 fetcher->SetRequestContext(context_getter);
145 fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
146 net::LOAD_DO_NOT_SAVE_COOKIES |
147 net::LOAD_DISABLE_CACHE);
[email protected]e8f96ff2011-08-03 05:07:33148 // TODO(cpu): Define our retry and backoff policy.
[email protected]7cc6e5632011-10-25 17:56:12149 fetcher->SetAutomaticallyRetryOn5xx(false);
[email protected]e8f96ff2011-08-03 05:07:33150 if (save_to_file) {
151 fetcher->SaveResponseToTemporaryFile(
152 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
153 }
154 fetcher->Start();
155}
156
157// Returs true if the url request of |fetcher| was succesful.
[email protected]10c2d692012-05-11 05:32:23158bool FetchSuccess(const net::URLFetcher& fetcher) {
[email protected]7cc6e5632011-10-25 17:56:12159 return (fetcher.GetStatus().status() == net::URLRequestStatus::SUCCESS) &&
160 (fetcher.GetResponseCode() == 200);
[email protected]e8f96ff2011-08-03 05:07:33161}
162
163// This is the one and only per-item state structure. Designed to be hosted
164// in a std::vector or a std::list. The two main members are |component|
165// which is supplied by the the component updater client and |status| which
166// is modified as the item is processed by the update pipeline. The expected
167// transition graph is:
168// error error error
169// +--kNoUpdate<------<-------+------<------+------<------+
170// | | | |
171// V yes | | |
172// kNew --->kChecking-->[update?]----->kCanUpdate-->kDownloading-->kUpdating
173// ^ | |
174// | |no |
175// |--kUpToDate<---+ |
176// | success |
177// +--kUpdated<-------------------------------------------+
178//
179struct CrxUpdateItem {
180 enum Status {
181 kNew,
182 kChecking,
183 kCanUpdate,
184 kDownloading,
185 kUpdating,
186 kUpdated,
187 kUpToDate,
188 kNoUpdate,
189 kLastStatus
190 };
191
192 Status status;
193 GURL crx_url;
194 std::string id;
195 base::Time last_check;
196 CrxComponent component;
[email protected]07f93af12011-08-17 20:57:22197 Version next_version;
[email protected]e8f96ff2011-08-03 05:07:33198
199 CrxUpdateItem() : status(kNew) {}
200
201 // Function object used to find a specific component.
202 class FindById {
203 public:
204 explicit FindById(const std::string& id) : id_(id) {}
205
206 bool operator() (CrxUpdateItem* item) const {
207 return (item->id == id_);
208 }
209 private:
210 const std::string& id_;
211 };
212};
213
214} // namespace.
215
216typedef ComponentUpdateService::Configurator Config;
217
[email protected]1e545b62012-01-24 17:01:10218CrxComponent::CrxComponent() : installer(NULL) {}
[email protected]e8f96ff2011-08-03 05:07:33219CrxComponent::~CrxComponent() {}
220
221//////////////////////////////////////////////////////////////////////////////
222// The one and only implementation of the ComponentUpdateService interface. In
223// charge of running the show. The main method is ProcessPendingItems() which
[email protected]50b01392012-09-01 03:33:13224// is called periodically to do the upgrades/installs or the update checks.
[email protected]e8f96ff2011-08-03 05:07:33225// An important consideration here is to be as "low impact" as we can to the
226// rest of the browser, so even if we have many components registered and
[email protected]f1050432012-02-15 01:35:46227// eligible for update, we only do one thing at a time with pauses in between
[email protected]e8f96ff2011-08-03 05:07:33228// the tasks. Also when we do network requests there is only one |url_fetcher_|
229// in flight at at a time.
230// There are no locks in this code, the main structure |work_items_| is mutated
231// only from the UI thread. The unpack and installation is done in the file
232// thread and the network requests are done in the IO thread and in the file
233// thread.
234class CrxUpdateService : public ComponentUpdateService {
235 public:
236 explicit CrxUpdateService(ComponentUpdateService::Configurator* config);
237
238 virtual ~CrxUpdateService();
239
240 // Overrides for ComponentUpdateService.
241 virtual Status Start() OVERRIDE;
242 virtual Status Stop() OVERRIDE;
243 virtual Status RegisterComponent(const CrxComponent& component) OVERRIDE;
244
245 // The only purpose of this class is to forward the
[email protected]c4f883a2012-02-03 17:02:07246 // UtilityProcessHostClient callbacks so CrxUpdateService does
[email protected]e8f96ff2011-08-03 05:07:33247 // not have to derive from it because that is refcounted.
[email protected]c4f883a2012-02-03 17:02:07248 class ManifestParserBridge : public UtilityProcessHostClient {
[email protected]e8f96ff2011-08-03 05:07:33249 public:
250 explicit ManifestParserBridge(CrxUpdateService* service)
251 : service_(service) {}
252
253 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
254 bool handled = true;
255 IPC_BEGIN_MESSAGE_MAP(ManifestParserBridge, message)
[email protected]2ccf45c2011-08-19 23:35:50256 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseUpdateManifest_Succeeded,
[email protected]e8f96ff2011-08-03 05:07:33257 OnParseUpdateManifestSucceeded)
[email protected]2ccf45c2011-08-19 23:35:50258 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseUpdateManifest_Failed,
[email protected]e8f96ff2011-08-03 05:07:33259 OnParseUpdateManifestFailed)
260 IPC_MESSAGE_UNHANDLED(handled = false)
261 IPC_END_MESSAGE_MAP()
262 return handled;
263 }
264
265 private:
[email protected]a3988cc2012-04-27 05:07:18266 virtual ~ManifestParserBridge() {}
267
[email protected]f1050432012-02-15 01:35:46268 // Omaha update response XML was successfully parsed.
[email protected]e8f96ff2011-08-03 05:07:33269 void OnParseUpdateManifestSucceeded(const UpdateManifest::Results& r) {
270 service_->OnParseUpdateManifestSucceeded(r);
271 }
272 // Omaha update response XML could not be parsed.
273 void OnParseUpdateManifestFailed(const std::string& e) {
274 service_->OnParseUpdateManifestFailed(e);
275 }
276
277 CrxUpdateService* service_;
278 DISALLOW_COPY_AND_ASSIGN(ManifestParserBridge);
279 };
280
281 // Context for a update check url request. See DelegateWithContext above.
282 struct UpdateContext {
283 base::Time start;
284 UpdateContext() : start(base::Time::Now()) {}
285 };
286
287 // Context for a crx download url request. See DelegateWithContext above.
288 struct CRXContext {
289 ComponentInstaller* installer;
290 std::vector<uint8> pk_hash;
291 std::string id;
292 CRXContext() : installer(NULL) {}
293 };
294
[email protected]10c2d692012-05-11 05:32:23295 void OnURLFetchComplete(const net::URLFetcher* source,
[email protected]7cc6e5632011-10-25 17:56:12296 UpdateContext* context);
[email protected]e8f96ff2011-08-03 05:07:33297
[email protected]10c2d692012-05-11 05:32:23298 void OnURLFetchComplete(const net::URLFetcher* source,
[email protected]7cc6e5632011-10-25 17:56:12299 CRXContext* context);
[email protected]e8f96ff2011-08-03 05:07:33300
301 private:
302 // See ManifestParserBridge.
303 void OnParseUpdateManifestSucceeded(
304 const UpdateManifest::Results& results);
305
306 // See ManifestParserBridge.
307 void OnParseUpdateManifestFailed(
308 const std::string& error_message);
309
310 bool AddItemToUpdateCheck(CrxUpdateItem* item, std::string* query);
311
312 void ProcessPendingItems();
313
314 void ScheduleNextRun(bool step_delay);
315
316 void ParseManifest(const std::string& xml);
317
318 void Install(const CRXContext* context, const FilePath& crx_path);
319
[email protected]07f93af12011-08-17 20:57:22320 void DoneInstalling(const std::string& component_id,
321 ComponentUnpacker::Error error);
[email protected]e8f96ff2011-08-03 05:07:33322
323 size_t ChangeItemStatus(CrxUpdateItem::Status from,
324 CrxUpdateItem::Status to);
325
326 CrxUpdateItem* FindUpdateItemById(const std::string& id);
327
328 scoped_ptr<Config> config_;
329
[email protected]d3ec669b2012-05-23 07:12:14330 scoped_ptr<net::URLFetcher> url_fetcher_;
[email protected]e8f96ff2011-08-03 05:07:33331
332 typedef std::vector<CrxUpdateItem*> UpdateItems;
333 UpdateItems work_items_;
334
335 base::OneShotTimer<CrxUpdateService> timer_;
336
337 Version chrome_version_;
338
339 bool running_;
340
341 DISALLOW_COPY_AND_ASSIGN(CrxUpdateService);
342};
343
[email protected]e8f96ff2011-08-03 05:07:33344//////////////////////////////////////////////////////////////////////////////
345
346CrxUpdateService::CrxUpdateService(
347 ComponentUpdateService::Configurator* config)
348 : config_(config),
349 chrome_version_(chrome::VersionInfo().Version()),
350 running_(false) {
351}
352
353CrxUpdateService::~CrxUpdateService() {
354 // Because we are a singleton, at this point only the UI thread should be
355 // alive, this simplifies the management of the work that could be in
356 // flight in other threads.
357 Stop();
358 STLDeleteElements(&work_items_);
359}
360
361ComponentUpdateService::Status CrxUpdateService::Start() {
362 // Note that RegisterComponent will call Start() when the first
363 // component is registered, so it can be called twice. This way
364 // we avoid scheduling the timer if there is no work to do.
365 running_ = true;
366 if (work_items_.empty())
367 return kOk;
368
[email protected]ad50def52011-10-19 23:17:07369 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33370 chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED,
[email protected]6c2381d2011-10-19 02:52:53371 content::Source<ComponentUpdateService>(this),
[email protected]ad50def52011-10-19 23:17:07372 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33373
[email protected]d323a172011-09-02 18:23:02374 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(config_->InitialDelay()),
[email protected]e8f96ff2011-08-03 05:07:33375 this, &CrxUpdateService::ProcessPendingItems);
376 return kOk;
377}
378
379// Stop the main check + update loop. In flight operations will be
380// completed.
381ComponentUpdateService::Status CrxUpdateService::Stop() {
382 running_ = false;
383 timer_.Stop();
384 return kOk;
385}
386
387// This function sets the timer which will call ProcessPendingItems() there
388// are two kind of waits, the short one (with step_delay = true) and the
389// long one.
390void CrxUpdateService::ScheduleNextRun(bool step_delay) {
391 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]cf442612011-08-09 20:20:12392 DCHECK(url_fetcher_.get() == NULL);
[email protected]e8f96ff2011-08-03 05:07:33393 CHECK(!timer_.IsRunning());
394 // It could be the case that Stop() had been called while a url request
395 // or unpacking was in flight, if so we arrive here but |running_| is
396 // false. In that case do not loop again.
397 if (!running_)
398 return;
399
[email protected]cf442612011-08-09 20:20:12400 int64 delay = step_delay ? config_->StepDelay() : config_->NextCheckDelay();
401
[email protected]e8f96ff2011-08-03 05:07:33402 if (!step_delay) {
[email protected]ad50def52011-10-19 23:17:07403 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33404 chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING,
[email protected]6c2381d2011-10-19 02:52:53405 content::Source<ComponentUpdateService>(this),
[email protected]ad50def52011-10-19 23:17:07406 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33407 // Zero is only used for unit tests.
[email protected]cf442612011-08-09 20:20:12408 if (0 == delay)
[email protected]e8f96ff2011-08-03 05:07:33409 return;
410 }
411
[email protected]d323a172011-09-02 18:23:02412 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(delay),
[email protected]e8f96ff2011-08-03 05:07:33413 this, &CrxUpdateService::ProcessPendingItems);
414}
415
416// Given a extension-like component id, find the associated component.
417CrxUpdateItem* CrxUpdateService::FindUpdateItemById(const std::string& id) {
418 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
419 CrxUpdateItem::FindById finder(id);
420 UpdateItems::iterator it = std::find_if(work_items_.begin(),
421 work_items_.end(),
422 finder);
423 if (it == work_items_.end())
424 return NULL;
425 return (*it);
426}
427
428// Changes all the components in |work_items_| that have |from| status to
429// |to| statatus and returns how many have been changed.
430size_t CrxUpdateService::ChangeItemStatus(CrxUpdateItem::Status from,
431 CrxUpdateItem::Status to) {
432 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
433 size_t count = 0;
434 for (UpdateItems::iterator it = work_items_.begin();
435 it != work_items_.end(); ++it) {
436 CrxUpdateItem* item = *it;
437 if (item->status != from)
438 continue;
439 item->status = to;
440 ++count;
441 }
442 return count;
443}
444
445// Adds a component to be checked for upgrades. If the component exists it
446// it will be replaced and the return code is kReplaced.
447//
448// TODO(cpu): Evaluate if we want to support un-registration.
449ComponentUpdateService::Status CrxUpdateService::RegisterComponent(
450 const CrxComponent& component) {
451 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
452 if (component.pk_hash.empty() ||
453 !component.version.IsValid() ||
454 !component.installer)
455 return kError;
456
457 std::string id =
458 HexStringToID(StringToLowerASCII(base::HexEncode(&component.pk_hash[0],
459 component.pk_hash.size()/2)));
460 CrxUpdateItem* uit;
461 uit = FindUpdateItemById(id);
462 if (uit) {
463 uit->component = component;
464 return kReplaced;
465 }
466
467 uit = new CrxUpdateItem;
468 uit->id.swap(id);
469 uit->component = component;
470 work_items_.push_back(uit);
471 // If this is the first component registered we call Start to
472 // schedule the first timer.
473 if (running_ && (work_items_.size() == 1))
474 Start();
475
476 return kOk;
477}
478
479// Sets a component to be checked for updates.
480// The componet to add is |crxit| and the |query| string is modified with the
481// required omaha compatible query. Returns false when the query strings
482// is longer than specified by UrlSizeLimit().
483bool CrxUpdateService::AddItemToUpdateCheck(CrxUpdateItem* item,
484 std::string* query) {
485 if (!AddQueryString(item->id,
486 item->component.version.GetString(),
487 config_->UrlSizeLimit(), query))
488 return false;
489 item->status = CrxUpdateItem::kChecking;
490 item->last_check = base::Time::Now();
491 return true;
492}
493
494// Here is where the work gets scheduled. Given that our |work_items_| list
495// is expected to be ten or less items, we simply loop several times.
496void CrxUpdateService::ProcessPendingItems() {
497 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
498 // First check for ready upgrades and do one. The first
499 // step is to fetch the crx package.
500 for (UpdateItems::const_iterator it = work_items_.begin();
501 it != work_items_.end(); ++it) {
502 CrxUpdateItem* item = *it;
503 if (item->status != CrxUpdateItem::kCanUpdate)
504 continue;
505 // Found component to update, start the process.
506 item->status = CrxUpdateItem::kDownloading;
507 CRXContext* context = new CRXContext;
508 context->pk_hash = item->component.pk_hash;
509 context->id = item->id;
510 context->installer = item->component.installer;
[email protected]3dc1bc42012-06-19 08:20:53511 url_fetcher_.reset(net::URLFetcher::Create(
[email protected]d3ec669b2012-05-23 07:12:14512 0, item->crx_url, net::URLFetcher::GET,
[email protected]e8f96ff2011-08-03 05:07:33513 MakeContextDelegate(this, context)));
514 StartFetch(url_fetcher_.get(), config_->RequestContext(), true);
515 return;
516 }
517
518 std::string query;
519 // If no pending upgrades, we check the if there are new
520 // components we have not checked against the server. We
521 // can batch a bunch in a single url request.
522 for (UpdateItems::const_iterator it = work_items_.begin();
523 it != work_items_.end(); ++it) {
524 CrxUpdateItem* item = *it;
525 if (item->status != CrxUpdateItem::kNew)
526 continue;
527 if (!AddItemToUpdateCheck(item, &query))
528 break;
529 }
530
531 // Next we can go back to components we already checked, here
532 // we can also batch them in a single url request, as long as
533 // we have not checked them recently.
[email protected]afa9e342012-04-06 02:51:32534 const base::TimeDelta min_delta_time =
[email protected]e8f96ff2011-08-03 05:07:33535 base::TimeDelta::FromSeconds(config_->MinimumReCheckWait());
536
537 for (UpdateItems::const_iterator it = work_items_.begin();
538 it != work_items_.end(); ++it) {
539 CrxUpdateItem* item = *it;
[email protected]cf442612011-08-09 20:20:12540 if ((item->status != CrxUpdateItem::kNoUpdate) &&
[email protected]e8f96ff2011-08-03 05:07:33541 (item->status != CrxUpdateItem::kUpToDate))
542 continue;
543 base::TimeDelta delta = base::Time::Now() - item->last_check;
544 if (delta < min_delta_time)
545 continue;
546 if (!AddItemToUpdateCheck(item, &query))
547 break;
548 }
549 // Finally, we check components that we already updated.
550 for (UpdateItems::const_iterator it = work_items_.begin();
551 it != work_items_.end(); ++it) {
552 CrxUpdateItem* item = *it;
553 if (item->status != CrxUpdateItem::kUpdated)
554 continue;
555 base::TimeDelta delta = base::Time::Now() - item->last_check;
556 if (delta < min_delta_time)
557 continue;
558 if (!AddItemToUpdateCheck(item, &query))
559 break;
560 }
561
562 if (query.empty()) {
563 // Next check after the long sleep.
564 ScheduleNextRun(false);
565 return;
566 }
567
568 // We got components to check. Start the url request.
[email protected]926d36332011-10-05 01:06:25569 const std::string full_query = MakeFinalQuery(config_->UpdateUrl().spec(),
570 query,
571 config_->ExtraRequestParams());
[email protected]3dc1bc42012-06-19 08:20:53572 url_fetcher_.reset(net::URLFetcher::Create(
[email protected]d3ec669b2012-05-23 07:12:14573 0, GURL(full_query), net::URLFetcher::GET,
[email protected]e8f96ff2011-08-03 05:07:33574 MakeContextDelegate(this, new UpdateContext())));
575 StartFetch(url_fetcher_.get(), config_->RequestContext(), false);
576}
577
578// Caled when we got a response from the update server. It consists of an xml
579// document following the omaha update scheme.
[email protected]10c2d692012-05-11 05:32:23580void CrxUpdateService::OnURLFetchComplete(const net::URLFetcher* source,
[email protected]07f93af12011-08-17 20:57:22581 UpdateContext* context) {
[email protected]e8f96ff2011-08-03 05:07:33582 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
583 if (FetchSuccess(*source)) {
584 std::string xml;
585 source->GetResponseAsString(&xml);
[email protected]cf442612011-08-09 20:20:12586 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33587 ParseManifest(xml);
588 } else {
[email protected]cf442612011-08-09 20:20:12589 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33590 CrxUpdateService::OnParseUpdateManifestFailed("network error");
591 }
[email protected]07f93af12011-08-17 20:57:22592 delete context;
[email protected]e8f96ff2011-08-03 05:07:33593}
594
595// Parsing the manifest is either done right now for tests or in a sandboxed
596// process for the production environment. This mitigates the case where an
597// attacker was able to feed us a malicious xml string.
598void CrxUpdateService::ParseManifest(const std::string& xml) {
599 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
600 if (config_->InProcess()) {
601 UpdateManifest manifest;
602 if (!manifest.Parse(xml)) {
603 CrxUpdateService::OnParseUpdateManifestFailed(manifest.errors());
604 } else {
605 CrxUpdateService::OnParseUpdateManifestSucceeded(manifest.results());
606 }
607 } else {
[email protected]c4f883a2012-02-03 17:02:07608 UtilityProcessHost* host = UtilityProcessHost::Create(
[email protected]7f8f24f2012-11-15 19:40:14609 new ManifestParserBridge(this),
610 base::MessageLoopProxy::current());
[email protected]c4f883a2012-02-03 17:02:07611 host->EnableZygote();
[email protected]2ccf45c2011-08-19 23:35:50612 host->Send(new ChromeUtilityMsg_ParseUpdateManifest(xml));
[email protected]e8f96ff2011-08-03 05:07:33613 }
614}
615
616// A valid Omaha update check has arrived, from only the list of components that
617// we are currently upgrading we check for a match in which the server side
618// version is newer, if so we queue them for an upgrade. The next time we call
619// ProcessPendingItems() one of them will be drafted for the upgrade process.
620void CrxUpdateService::OnParseUpdateManifestSucceeded(
621 const UpdateManifest::Results& results) {
622 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]a1f8c162012-09-27 22:32:21623 size_t update_pending = 0;
[email protected]e8f96ff2011-08-03 05:07:33624 std::vector<UpdateManifest::Result>::const_iterator it;
625 for (it = results.list.begin(); it != results.list.end(); ++it) {
626 CrxUpdateItem* crx = FindUpdateItemById(it->extension_id);
627 if (!crx)
628 continue;
629
630 if (crx->status != CrxUpdateItem::kChecking)
631 continue; // Not updating this component now.
632
[email protected]360b8bb2011-09-01 21:48:06633 config_->OnEvent(Configurator::kManifestCheck, CrxIdtoUMAId(crx->id));
634
[email protected]e8f96ff2011-08-03 05:07:33635 if (it->version.empty()) {
[email protected]cf442612011-08-09 20:20:12636 // No version means no update available.
[email protected]e8f96ff2011-08-03 05:07:33637 crx->status = CrxUpdateItem::kNoUpdate;
[email protected]cf442612011-08-09 20:20:12638 continue;
[email protected]e8f96ff2011-08-03 05:07:33639 }
640 if (!IsVersionNewer(crx->component.version, it->version)) {
[email protected]cf442612011-08-09 20:20:12641 // Our component is up to date.
[email protected]e8f96ff2011-08-03 05:07:33642 crx->status = CrxUpdateItem::kUpToDate;
[email protected]cf442612011-08-09 20:20:12643 continue;
[email protected]e8f96ff2011-08-03 05:07:33644 }
645 if (!it->browser_min_version.empty()) {
[email protected]cf442612011-08-09 20:20:12646 if (IsVersionNewer(chrome_version_, it->browser_min_version)) {
647 // Does not apply for this chrome version.
648 crx->status = CrxUpdateItem::kNoUpdate;
649 continue;
650 }
[email protected]e8f96ff2011-08-03 05:07:33651 }
652 // All test passed. Queue an upgrade for this component and fire the
653 // notifications.
654 crx->crx_url = it->crx_url;
655 crx->status = CrxUpdateItem::kCanUpdate;
[email protected]07f93af12011-08-17 20:57:22656 crx->next_version = Version(it->version);
[email protected]e8f96ff2011-08-03 05:07:33657 ++update_pending;
658
[email protected]ad50def52011-10-19 23:17:07659 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33660 chrome::NOTIFICATION_COMPONENT_UPDATE_FOUND,
[email protected]6c2381d2011-10-19 02:52:53661 content::Source<std::string>(&crx->id),
[email protected]ad50def52011-10-19 23:17:07662 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33663 }
[email protected]cf442612011-08-09 20:20:12664
665 // All the components that are not mentioned in the manifest we
666 // consider them up to date.
667 ChangeItemStatus(CrxUpdateItem::kChecking, CrxUpdateItem::kUpToDate);
668
[email protected]e8f96ff2011-08-03 05:07:33669 // If there are updates pending we do a short wait.
[email protected]a1f8c162012-09-27 22:32:21670 ScheduleNextRun(update_pending > 0);
[email protected]e8f96ff2011-08-03 05:07:33671}
672
673void CrxUpdateService::OnParseUpdateManifestFailed(
674 const std::string& error_message) {
675 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
676 size_t count = ChangeItemStatus(CrxUpdateItem::kChecking,
677 CrxUpdateItem::kNoUpdate);
[email protected]360b8bb2011-09-01 21:48:06678 config_->OnEvent(Configurator::kManifestError, static_cast<int>(count));
[email protected]e8f96ff2011-08-03 05:07:33679 DCHECK_GT(count, 0ul);
680 ScheduleNextRun(false);
681}
682
683// Called when the CRX package has been downloaded to a temporary location.
684// Here we fire the notifications and schedule the component-specific installer
685// to be called in the file thread.
[email protected]10c2d692012-05-11 05:32:23686void CrxUpdateService::OnURLFetchComplete(const net::URLFetcher* source,
[email protected]e8f96ff2011-08-03 05:07:33687 CRXContext* context) {
688 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
689 base::PlatformFileError error_code;
690
691 if (source->FileErrorOccurred(&error_code) || !FetchSuccess(*source)) {
692 size_t count = ChangeItemStatus(CrxUpdateItem::kDownloading,
693 CrxUpdateItem::kNoUpdate);
694 DCHECK_EQ(count, 1ul);
[email protected]360b8bb2011-09-01 21:48:06695 config_->OnEvent(Configurator::kNetworkError, CrxIdtoUMAId(context->id));
[email protected]cf442612011-08-09 20:20:12696 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33697 ScheduleNextRun(false);
698 } else {
699 FilePath temp_crx_path;
700 CHECK(source->GetResponseAsFilePath(true, &temp_crx_path));
701 size_t count = ChangeItemStatus(CrxUpdateItem::kDownloading,
702 CrxUpdateItem::kUpdating);
703 DCHECK_EQ(count, 1ul);
[email protected]cf442612011-08-09 20:20:12704 url_fetcher_.reset();
705
[email protected]ad50def52011-10-19 23:17:07706 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33707 chrome::NOTIFICATION_COMPONENT_UPDATE_READY,
[email protected]6c2381d2011-10-19 02:52:53708 content::Source<std::string>(&context->id),
[email protected]ad50def52011-10-19 23:17:07709 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33710
[email protected]44da56e2011-11-21 19:59:14711 // Why unretained? See comment at top of file.
[email protected]73251e72012-03-04 02:10:33712 BrowserThread::PostDelayedTask(
713 BrowserThread::FILE,
714 FROM_HERE,
[email protected]44da56e2011-11-21 19:59:14715 base::Bind(&CrxUpdateService::Install,
716 base::Unretained(this),
717 context,
718 temp_crx_path),
[email protected]73251e72012-03-04 02:10:33719 base::TimeDelta::FromMilliseconds(config_->StepDelay()));
[email protected]e8f96ff2011-08-03 05:07:33720 }
[email protected]e8f96ff2011-08-03 05:07:33721}
722
723// Install consists of digital signature verification, unpacking and then
724// calling the component specific installer. All that is handled by the
725// |unpacker|. If there is an error this function is in charge of deleting
726// the files created.
727void CrxUpdateService::Install(const CRXContext* context,
728 const FilePath& crx_path) {
729 // This function owns the |crx_path| and the |context| object.
730 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
731 ComponentUnpacker
732 unpacker(context->pk_hash, crx_path, context->installer);
[email protected]e8f96ff2011-08-03 05:07:33733 if (!file_util::Delete(crx_path, false)) {
734 NOTREACHED() << crx_path.value();
735 }
[email protected]44da56e2011-11-21 19:59:14736 // Why unretained? See comment at top of file.
[email protected]73251e72012-03-04 02:10:33737 BrowserThread::PostDelayedTask(
738 BrowserThread::UI,
739 FROM_HERE,
[email protected]44da56e2011-11-21 19:59:14740 base::Bind(&CrxUpdateService::DoneInstalling, base::Unretained(this),
741 context->id, unpacker.error()),
[email protected]73251e72012-03-04 02:10:33742 base::TimeDelta::FromMilliseconds(config_->StepDelay()));
[email protected]07f93af12011-08-17 20:57:22743 delete context;
[email protected]e8f96ff2011-08-03 05:07:33744}
745
746// Installation has been completed. Adjust the component status and
747// schedule the next check.
[email protected]07f93af12011-08-17 20:57:22748void CrxUpdateService::DoneInstalling(const std::string& component_id,
749 ComponentUnpacker::Error error) {
[email protected]e8f96ff2011-08-03 05:07:33750 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]07f93af12011-08-17 20:57:22751
752 CrxUpdateItem* item = FindUpdateItemById(component_id);
753 item->status = (error == ComponentUnpacker::kNone) ? CrxUpdateItem::kUpdated :
754 CrxUpdateItem::kNoUpdate;
755 if (item->status == CrxUpdateItem::kUpdated)
756 item->component.version = item->next_version;
757
[email protected]360b8bb2011-09-01 21:48:06758 Configurator::Events event;
759 switch (error) {
760 case ComponentUnpacker::kNone:
761 event = Configurator::kComponentUpdated;
762 break;
763 case ComponentUnpacker::kInstallerError:
764 event = Configurator::kInstallerError;
765 break;
766 default:
767 event = Configurator::kUnpackError;
768 break;
769 }
770
771 config_->OnEvent(event, CrxIdtoUMAId(component_id));
[email protected]e8f96ff2011-08-03 05:07:33772 ScheduleNextRun(false);
773}
774
775// The component update factory. Using the component updater as a singleton
776// is the job of the browser process.
777ComponentUpdateService* ComponentUpdateServiceFactory(
778 ComponentUpdateService::Configurator* config) {
779 DCHECK(config);
780 return new CrxUpdateService(config);
781}