blob: b24d100597282e3c804e96643b920926b6a2db8a [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]ad50def52011-10-19 23:17:0728#include "content/public/browser/notification_service.h"
[email protected]c4f883a2012-02-03 17:02:0729#include "content/public/browser/utility_process_host.h"
30#include "content/public/browser/utility_process_host_client.h"
[email protected]c530c852011-10-24 18:18:3431#include "content/public/common/url_fetcher_delegate.h"
[email protected]36aea2702011-10-26 01:12:2232#include "content/public/common/url_fetcher.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"
36
[email protected]631bb742011-11-02 11:29:3937using content::BrowserThread;
[email protected]c4f883a2012-02-03 17:02:0738using content::UtilityProcessHost;
39using content::UtilityProcessHostClient;
[email protected]631bb742011-11-02 11:29:3940
[email protected]44da56e2011-11-21 19:59:1441// The component updater is designed to live until process shutdown, so
42// base::Bind() calls are not refcounted.
43
[email protected]e8f96ff2011-08-03 05:07:3344namespace {
45// Extends an omaha compatible update check url |query| string. Does
46// not mutate the string if it would be longer than |limit| chars.
[email protected]4c37b452011-12-21 01:33:5247bool AddQueryString(const std::string& id,
48 const std::string& version,
49 size_t limit,
50 std::string* query) {
[email protected]e8f96ff2011-08-03 05:07:3351 std::string additional =
52 base::StringPrintf("id=%s&v=%s&uc", id.c_str(), version.c_str());
[email protected]4a19be92011-09-22 14:25:0253 additional = "x=" + net::EscapeQueryParamValue(additional, true);
[email protected]e8f96ff2011-08-03 05:07:3354 if ((additional.size() + query->size() + 1) > limit)
55 return false;
[email protected]926d36332011-10-05 01:06:2556 if (!query->empty())
57 query->append(1, '&');
[email protected]e8f96ff2011-08-03 05:07:3358 query->append(additional);
59 return true;
60}
61
[email protected]926d36332011-10-05 01:06:2562// Create the final omaha compatible query. The |extra| is optional and can
63// be null. It should contain top level (non-escaped) parameters.
64std::string MakeFinalQuery(const std::string& host,
65 const std::string& query,
66 const char* extra) {
67 std::string request(host);
68 request.append(1, '?');
69 if (extra) {
70 request.append(extra);
71 request.append(1, '&');
72 }
73 request.append(query);
74 return request;
75}
76
[email protected]e8f96ff2011-08-03 05:07:3377// Produces an extension-like friendly |id|. This might be removed in the
78// future if we roll our on packing tools.
79static std::string HexStringToID(const std::string& hexstr) {
80 std::string id;
81 for (size_t i = 0; i < hexstr.size(); ++i) {
82 int val;
[email protected]eb72b272011-12-19 16:10:5583 if (base::HexStringToInt(base::StringPiece(hexstr.begin() + i,
84 hexstr.begin() + i + 1),
85 &val)) {
[email protected]e8f96ff2011-08-03 05:07:3386 id.append(1, val + 'a');
[email protected]eb72b272011-12-19 16:10:5587 } else {
[email protected]e8f96ff2011-08-03 05:07:3388 id.append(1, 'a');
[email protected]eb72b272011-12-19 16:10:5589 }
[email protected]e8f96ff2011-08-03 05:07:3390 }
91 DCHECK(Extension::IdIsValid(id));
92 return id;
93}
94
[email protected]360b8bb2011-09-01 21:48:0695// Returns given a crx id it returns a small number, less than 100, that has a
96// decent chance of being unique among the registered components. It also has
97// the nice property that can be trivially computed by hand.
98static int CrxIdtoUMAId(const std::string& id) {
99 CHECK(id.size() > 2);
100 return id[0] + id[1] + id[2] - ('a' * 3);
101}
102
[email protected]e8f96ff2011-08-03 05:07:33103// Helper to do version check for components.
104bool IsVersionNewer(const Version& current, const std::string& proposed) {
105 Version proposed_ver(proposed);
106 if (!proposed_ver.IsValid())
107 return false;
108 return (current.CompareTo(proposed_ver) < 0);
109}
110
111// Helper template class that allows our main class to have separate
[email protected]f1050432012-02-15 01:35:46112// OnURLFetchComplete() callbacks for different types of url requests
[email protected]e8f96ff2011-08-03 05:07:33113// they are differentiated by the |Ctx| type.
114template <typename Del, typename Ctx>
[email protected]c530c852011-10-24 18:18:34115class DelegateWithContext : public content::URLFetcherDelegate {
[email protected]e8f96ff2011-08-03 05:07:33116 public:
117 DelegateWithContext(Del* delegate, Ctx* context)
118 : delegate_(delegate), context_(context) {}
119
[email protected]7cc6e5632011-10-25 17:56:12120 virtual void OnURLFetchComplete(const content::URLFetcher* source) OVERRIDE {
[email protected]e8f96ff2011-08-03 05:07:33121 delegate_->OnURLFetchComplete(source, context_);
122 delete this;
123 }
124
125 private:
126 ~DelegateWithContext() {}
127
128 Del* delegate_;
129 Ctx* context_;
130};
131// This function creates the right DelegateWithContext using template inference.
132template <typename Del, typename Ctx>
[email protected]c530c852011-10-24 18:18:34133content::URLFetcherDelegate* MakeContextDelegate(Del* delegate, Ctx* context) {
[email protected]e8f96ff2011-08-03 05:07:33134 return new DelegateWithContext<Del, Ctx>(delegate, context);
135}
136
137// Helper to start a url request using |fetcher| with the common flags.
[email protected]7cc6e5632011-10-25 17:56:12138void StartFetch(content::URLFetcher* fetcher,
[email protected]e8f96ff2011-08-03 05:07:33139 net::URLRequestContextGetter* context_getter,
140 bool save_to_file) {
[email protected]7cc6e5632011-10-25 17:56:12141 fetcher->SetRequestContext(context_getter);
142 fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
143 net::LOAD_DO_NOT_SAVE_COOKIES |
144 net::LOAD_DISABLE_CACHE);
[email protected]e8f96ff2011-08-03 05:07:33145 // TODO(cpu): Define our retry and backoff policy.
[email protected]7cc6e5632011-10-25 17:56:12146 fetcher->SetAutomaticallyRetryOn5xx(false);
[email protected]e8f96ff2011-08-03 05:07:33147 if (save_to_file) {
148 fetcher->SaveResponseToTemporaryFile(
149 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
150 }
151 fetcher->Start();
152}
153
154// Returs true if the url request of |fetcher| was succesful.
[email protected]7cc6e5632011-10-25 17:56:12155bool FetchSuccess(const content::URLFetcher& fetcher) {
156 return (fetcher.GetStatus().status() == net::URLRequestStatus::SUCCESS) &&
157 (fetcher.GetResponseCode() == 200);
[email protected]e8f96ff2011-08-03 05:07:33158}
159
160// This is the one and only per-item state structure. Designed to be hosted
161// in a std::vector or a std::list. The two main members are |component|
162// which is supplied by the the component updater client and |status| which
163// is modified as the item is processed by the update pipeline. The expected
164// transition graph is:
165// error error error
166// +--kNoUpdate<------<-------+------<------+------<------+
167// | | | |
168// V yes | | |
169// kNew --->kChecking-->[update?]----->kCanUpdate-->kDownloading-->kUpdating
170// ^ | |
171// | |no |
172// |--kUpToDate<---+ |
173// | success |
174// +--kUpdated<-------------------------------------------+
175//
176struct CrxUpdateItem {
177 enum Status {
178 kNew,
179 kChecking,
180 kCanUpdate,
181 kDownloading,
182 kUpdating,
183 kUpdated,
184 kUpToDate,
185 kNoUpdate,
186 kLastStatus
187 };
188
189 Status status;
190 GURL crx_url;
191 std::string id;
192 base::Time last_check;
193 CrxComponent component;
[email protected]07f93af12011-08-17 20:57:22194 Version next_version;
[email protected]e8f96ff2011-08-03 05:07:33195
196 CrxUpdateItem() : status(kNew) {}
197
198 // Function object used to find a specific component.
199 class FindById {
200 public:
201 explicit FindById(const std::string& id) : id_(id) {}
202
203 bool operator() (CrxUpdateItem* item) const {
204 return (item->id == id_);
205 }
206 private:
207 const std::string& id_;
208 };
209};
210
211} // namespace.
212
213typedef ComponentUpdateService::Configurator Config;
214
[email protected]1e545b62012-01-24 17:01:10215CrxComponent::CrxComponent() : installer(NULL) {}
[email protected]e8f96ff2011-08-03 05:07:33216CrxComponent::~CrxComponent() {}
217
218//////////////////////////////////////////////////////////////////////////////
219// The one and only implementation of the ComponentUpdateService interface. In
220// charge of running the show. The main method is ProcessPendingItems() which
221// is called periodically to do the updgrades/installs or the update checks.
222// An important consideration here is to be as "low impact" as we can to the
223// rest of the browser, so even if we have many components registered and
[email protected]f1050432012-02-15 01:35:46224// eligible for update, we only do one thing at a time with pauses in between
[email protected]e8f96ff2011-08-03 05:07:33225// the tasks. Also when we do network requests there is only one |url_fetcher_|
226// in flight at at a time.
227// There are no locks in this code, the main structure |work_items_| is mutated
228// only from the UI thread. The unpack and installation is done in the file
229// thread and the network requests are done in the IO thread and in the file
230// thread.
231class CrxUpdateService : public ComponentUpdateService {
232 public:
233 explicit CrxUpdateService(ComponentUpdateService::Configurator* config);
234
235 virtual ~CrxUpdateService();
236
237 // Overrides for ComponentUpdateService.
238 virtual Status Start() OVERRIDE;
239 virtual Status Stop() OVERRIDE;
240 virtual Status RegisterComponent(const CrxComponent& component) OVERRIDE;
241
242 // The only purpose of this class is to forward the
[email protected]c4f883a2012-02-03 17:02:07243 // UtilityProcessHostClient callbacks so CrxUpdateService does
[email protected]e8f96ff2011-08-03 05:07:33244 // not have to derive from it because that is refcounted.
[email protected]c4f883a2012-02-03 17:02:07245 class ManifestParserBridge : public UtilityProcessHostClient {
[email protected]e8f96ff2011-08-03 05:07:33246 public:
247 explicit ManifestParserBridge(CrxUpdateService* service)
248 : service_(service) {}
249
250 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
251 bool handled = true;
252 IPC_BEGIN_MESSAGE_MAP(ManifestParserBridge, message)
[email protected]2ccf45c2011-08-19 23:35:50253 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseUpdateManifest_Succeeded,
[email protected]e8f96ff2011-08-03 05:07:33254 OnParseUpdateManifestSucceeded)
[email protected]2ccf45c2011-08-19 23:35:50255 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseUpdateManifest_Failed,
[email protected]e8f96ff2011-08-03 05:07:33256 OnParseUpdateManifestFailed)
257 IPC_MESSAGE_UNHANDLED(handled = false)
258 IPC_END_MESSAGE_MAP()
259 return handled;
260 }
261
262 private:
[email protected]a3988cc2012-04-27 05:07:18263 virtual ~ManifestParserBridge() {}
264
[email protected]f1050432012-02-15 01:35:46265 // Omaha update response XML was successfully parsed.
[email protected]e8f96ff2011-08-03 05:07:33266 void OnParseUpdateManifestSucceeded(const UpdateManifest::Results& r) {
267 service_->OnParseUpdateManifestSucceeded(r);
268 }
269 // Omaha update response XML could not be parsed.
270 void OnParseUpdateManifestFailed(const std::string& e) {
271 service_->OnParseUpdateManifestFailed(e);
272 }
273
274 CrxUpdateService* service_;
275 DISALLOW_COPY_AND_ASSIGN(ManifestParserBridge);
276 };
277
278 // Context for a update check url request. See DelegateWithContext above.
279 struct UpdateContext {
280 base::Time start;
281 UpdateContext() : start(base::Time::Now()) {}
282 };
283
284 // Context for a crx download url request. See DelegateWithContext above.
285 struct CRXContext {
286 ComponentInstaller* installer;
287 std::vector<uint8> pk_hash;
288 std::string id;
289 CRXContext() : installer(NULL) {}
290 };
291
[email protected]7cc6e5632011-10-25 17:56:12292 void OnURLFetchComplete(const content::URLFetcher* source,
293 UpdateContext* context);
[email protected]e8f96ff2011-08-03 05:07:33294
[email protected]7cc6e5632011-10-25 17:56:12295 void OnURLFetchComplete(const content::URLFetcher* source,
296 CRXContext* context);
[email protected]e8f96ff2011-08-03 05:07:33297
298 private:
299 // See ManifestParserBridge.
300 void OnParseUpdateManifestSucceeded(
301 const UpdateManifest::Results& results);
302
303 // See ManifestParserBridge.
304 void OnParseUpdateManifestFailed(
305 const std::string& error_message);
306
307 bool AddItemToUpdateCheck(CrxUpdateItem* item, std::string* query);
308
309 void ProcessPendingItems();
310
311 void ScheduleNextRun(bool step_delay);
312
313 void ParseManifest(const std::string& xml);
314
315 void Install(const CRXContext* context, const FilePath& crx_path);
316
[email protected]07f93af12011-08-17 20:57:22317 void DoneInstalling(const std::string& component_id,
318 ComponentUnpacker::Error error);
[email protected]e8f96ff2011-08-03 05:07:33319
320 size_t ChangeItemStatus(CrxUpdateItem::Status from,
321 CrxUpdateItem::Status to);
322
323 CrxUpdateItem* FindUpdateItemById(const std::string& id);
324
325 scoped_ptr<Config> config_;
326
[email protected]7cc6e5632011-10-25 17:56:12327 scoped_ptr<content::URLFetcher> url_fetcher_;
[email protected]e8f96ff2011-08-03 05:07:33328
329 typedef std::vector<CrxUpdateItem*> UpdateItems;
330 UpdateItems work_items_;
331
332 base::OneShotTimer<CrxUpdateService> timer_;
333
334 Version chrome_version_;
335
336 bool running_;
337
338 DISALLOW_COPY_AND_ASSIGN(CrxUpdateService);
339};
340
[email protected]e8f96ff2011-08-03 05:07:33341//////////////////////////////////////////////////////////////////////////////
342
343CrxUpdateService::CrxUpdateService(
344 ComponentUpdateService::Configurator* config)
345 : config_(config),
346 chrome_version_(chrome::VersionInfo().Version()),
347 running_(false) {
348}
349
350CrxUpdateService::~CrxUpdateService() {
351 // Because we are a singleton, at this point only the UI thread should be
352 // alive, this simplifies the management of the work that could be in
353 // flight in other threads.
354 Stop();
355 STLDeleteElements(&work_items_);
356}
357
358ComponentUpdateService::Status CrxUpdateService::Start() {
359 // Note that RegisterComponent will call Start() when the first
360 // component is registered, so it can be called twice. This way
361 // we avoid scheduling the timer if there is no work to do.
362 running_ = true;
363 if (work_items_.empty())
364 return kOk;
365
[email protected]ad50def52011-10-19 23:17:07366 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33367 chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED,
[email protected]6c2381d2011-10-19 02:52:53368 content::Source<ComponentUpdateService>(this),
[email protected]ad50def52011-10-19 23:17:07369 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33370
[email protected]d323a172011-09-02 18:23:02371 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(config_->InitialDelay()),
[email protected]e8f96ff2011-08-03 05:07:33372 this, &CrxUpdateService::ProcessPendingItems);
373 return kOk;
374}
375
376// Stop the main check + update loop. In flight operations will be
377// completed.
378ComponentUpdateService::Status CrxUpdateService::Stop() {
379 running_ = false;
380 timer_.Stop();
381 return kOk;
382}
383
384// This function sets the timer which will call ProcessPendingItems() there
385// are two kind of waits, the short one (with step_delay = true) and the
386// long one.
387void CrxUpdateService::ScheduleNextRun(bool step_delay) {
388 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]cf442612011-08-09 20:20:12389 DCHECK(url_fetcher_.get() == NULL);
[email protected]e8f96ff2011-08-03 05:07:33390 CHECK(!timer_.IsRunning());
391 // It could be the case that Stop() had been called while a url request
392 // or unpacking was in flight, if so we arrive here but |running_| is
393 // false. In that case do not loop again.
394 if (!running_)
395 return;
396
[email protected]cf442612011-08-09 20:20:12397 int64 delay = step_delay ? config_->StepDelay() : config_->NextCheckDelay();
398
[email protected]e8f96ff2011-08-03 05:07:33399 if (!step_delay) {
[email protected]ad50def52011-10-19 23:17:07400 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33401 chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING,
[email protected]6c2381d2011-10-19 02:52:53402 content::Source<ComponentUpdateService>(this),
[email protected]ad50def52011-10-19 23:17:07403 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33404 // Zero is only used for unit tests.
[email protected]cf442612011-08-09 20:20:12405 if (0 == delay)
[email protected]e8f96ff2011-08-03 05:07:33406 return;
407 }
408
[email protected]d323a172011-09-02 18:23:02409 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(delay),
[email protected]e8f96ff2011-08-03 05:07:33410 this, &CrxUpdateService::ProcessPendingItems);
411}
412
413// Given a extension-like component id, find the associated component.
414CrxUpdateItem* CrxUpdateService::FindUpdateItemById(const std::string& id) {
415 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
416 CrxUpdateItem::FindById finder(id);
417 UpdateItems::iterator it = std::find_if(work_items_.begin(),
418 work_items_.end(),
419 finder);
420 if (it == work_items_.end())
421 return NULL;
422 return (*it);
423}
424
425// Changes all the components in |work_items_| that have |from| status to
426// |to| statatus and returns how many have been changed.
427size_t CrxUpdateService::ChangeItemStatus(CrxUpdateItem::Status from,
428 CrxUpdateItem::Status to) {
429 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
430 size_t count = 0;
431 for (UpdateItems::iterator it = work_items_.begin();
432 it != work_items_.end(); ++it) {
433 CrxUpdateItem* item = *it;
434 if (item->status != from)
435 continue;
436 item->status = to;
437 ++count;
438 }
439 return count;
440}
441
442// Adds a component to be checked for upgrades. If the component exists it
443// it will be replaced and the return code is kReplaced.
444//
445// TODO(cpu): Evaluate if we want to support un-registration.
446ComponentUpdateService::Status CrxUpdateService::RegisterComponent(
447 const CrxComponent& component) {
448 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
449 if (component.pk_hash.empty() ||
450 !component.version.IsValid() ||
451 !component.installer)
452 return kError;
453
454 std::string id =
455 HexStringToID(StringToLowerASCII(base::HexEncode(&component.pk_hash[0],
456 component.pk_hash.size()/2)));
457 CrxUpdateItem* uit;
458 uit = FindUpdateItemById(id);
459 if (uit) {
460 uit->component = component;
461 return kReplaced;
462 }
463
464 uit = new CrxUpdateItem;
465 uit->id.swap(id);
466 uit->component = component;
467 work_items_.push_back(uit);
468 // If this is the first component registered we call Start to
469 // schedule the first timer.
470 if (running_ && (work_items_.size() == 1))
471 Start();
472
473 return kOk;
474}
475
476// Sets a component to be checked for updates.
477// The componet to add is |crxit| and the |query| string is modified with the
478// required omaha compatible query. Returns false when the query strings
479// is longer than specified by UrlSizeLimit().
480bool CrxUpdateService::AddItemToUpdateCheck(CrxUpdateItem* item,
481 std::string* query) {
482 if (!AddQueryString(item->id,
483 item->component.version.GetString(),
484 config_->UrlSizeLimit(), query))
485 return false;
486 item->status = CrxUpdateItem::kChecking;
487 item->last_check = base::Time::Now();
488 return true;
489}
490
491// Here is where the work gets scheduled. Given that our |work_items_| list
492// is expected to be ten or less items, we simply loop several times.
493void CrxUpdateService::ProcessPendingItems() {
494 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
495 // First check for ready upgrades and do one. The first
496 // step is to fetch the crx package.
497 for (UpdateItems::const_iterator it = work_items_.begin();
498 it != work_items_.end(); ++it) {
499 CrxUpdateItem* item = *it;
500 if (item->status != CrxUpdateItem::kCanUpdate)
501 continue;
502 // Found component to update, start the process.
503 item->status = CrxUpdateItem::kDownloading;
504 CRXContext* context = new CRXContext;
505 context->pk_hash = item->component.pk_hash;
506 context->id = item->id;
507 context->installer = item->component.installer;
[email protected]36aea2702011-10-26 01:12:22508 url_fetcher_.reset(content::URLFetcher::Create(
509 0, item->crx_url, content::URLFetcher::GET,
[email protected]e8f96ff2011-08-03 05:07:33510 MakeContextDelegate(this, context)));
511 StartFetch(url_fetcher_.get(), config_->RequestContext(), true);
512 return;
513 }
514
515 std::string query;
516 // If no pending upgrades, we check the if there are new
517 // components we have not checked against the server. We
518 // can batch a bunch in a single url request.
519 for (UpdateItems::const_iterator it = work_items_.begin();
520 it != work_items_.end(); ++it) {
521 CrxUpdateItem* item = *it;
522 if (item->status != CrxUpdateItem::kNew)
523 continue;
524 if (!AddItemToUpdateCheck(item, &query))
525 break;
526 }
527
528 // Next we can go back to components we already checked, here
529 // we can also batch them in a single url request, as long as
530 // we have not checked them recently.
[email protected]afa9e342012-04-06 02:51:32531 const base::TimeDelta min_delta_time =
[email protected]e8f96ff2011-08-03 05:07:33532 base::TimeDelta::FromSeconds(config_->MinimumReCheckWait());
533
534 for (UpdateItems::const_iterator it = work_items_.begin();
535 it != work_items_.end(); ++it) {
536 CrxUpdateItem* item = *it;
[email protected]cf442612011-08-09 20:20:12537 if ((item->status != CrxUpdateItem::kNoUpdate) &&
[email protected]e8f96ff2011-08-03 05:07:33538 (item->status != CrxUpdateItem::kUpToDate))
539 continue;
540 base::TimeDelta delta = base::Time::Now() - item->last_check;
541 if (delta < min_delta_time)
542 continue;
543 if (!AddItemToUpdateCheck(item, &query))
544 break;
545 }
546 // Finally, we check components that we already updated.
547 for (UpdateItems::const_iterator it = work_items_.begin();
548 it != work_items_.end(); ++it) {
549 CrxUpdateItem* item = *it;
550 if (item->status != CrxUpdateItem::kUpdated)
551 continue;
552 base::TimeDelta delta = base::Time::Now() - item->last_check;
553 if (delta < min_delta_time)
554 continue;
555 if (!AddItemToUpdateCheck(item, &query))
556 break;
557 }
558
559 if (query.empty()) {
560 // Next check after the long sleep.
561 ScheduleNextRun(false);
562 return;
563 }
564
565 // We got components to check. Start the url request.
[email protected]926d36332011-10-05 01:06:25566 const std::string full_query = MakeFinalQuery(config_->UpdateUrl().spec(),
567 query,
568 config_->ExtraRequestParams());
[email protected]36aea2702011-10-26 01:12:22569 url_fetcher_.reset(content::URLFetcher::Create(
570 0, GURL(full_query), content::URLFetcher::GET,
[email protected]e8f96ff2011-08-03 05:07:33571 MakeContextDelegate(this, new UpdateContext())));
572 StartFetch(url_fetcher_.get(), config_->RequestContext(), false);
573}
574
575// Caled when we got a response from the update server. It consists of an xml
576// document following the omaha update scheme.
[email protected]7cc6e5632011-10-25 17:56:12577void CrxUpdateService::OnURLFetchComplete(const content::URLFetcher* source,
[email protected]07f93af12011-08-17 20:57:22578 UpdateContext* context) {
[email protected]e8f96ff2011-08-03 05:07:33579 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
580 if (FetchSuccess(*source)) {
581 std::string xml;
582 source->GetResponseAsString(&xml);
[email protected]cf442612011-08-09 20:20:12583 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33584 ParseManifest(xml);
585 } else {
[email protected]cf442612011-08-09 20:20:12586 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33587 CrxUpdateService::OnParseUpdateManifestFailed("network error");
588 }
[email protected]07f93af12011-08-17 20:57:22589 delete context;
[email protected]e8f96ff2011-08-03 05:07:33590}
591
592// Parsing the manifest is either done right now for tests or in a sandboxed
593// process for the production environment. This mitigates the case where an
594// attacker was able to feed us a malicious xml string.
595void CrxUpdateService::ParseManifest(const std::string& xml) {
596 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
597 if (config_->InProcess()) {
598 UpdateManifest manifest;
599 if (!manifest.Parse(xml)) {
600 CrxUpdateService::OnParseUpdateManifestFailed(manifest.errors());
601 } else {
602 CrxUpdateService::OnParseUpdateManifestSucceeded(manifest.results());
603 }
604 } else {
[email protected]c4f883a2012-02-03 17:02:07605 UtilityProcessHost* host = UtilityProcessHost::Create(
606 new ManifestParserBridge(this), BrowserThread::UI);
607 host->EnableZygote();
[email protected]2ccf45c2011-08-19 23:35:50608 host->Send(new ChromeUtilityMsg_ParseUpdateManifest(xml));
[email protected]e8f96ff2011-08-03 05:07:33609 }
610}
611
612// A valid Omaha update check has arrived, from only the list of components that
613// we are currently upgrading we check for a match in which the server side
614// version is newer, if so we queue them for an upgrade. The next time we call
615// ProcessPendingItems() one of them will be drafted for the upgrade process.
616void CrxUpdateService::OnParseUpdateManifestSucceeded(
617 const UpdateManifest::Results& results) {
618 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
619 int update_pending = 0;
620 std::vector<UpdateManifest::Result>::const_iterator it;
621 for (it = results.list.begin(); it != results.list.end(); ++it) {
622 CrxUpdateItem* crx = FindUpdateItemById(it->extension_id);
623 if (!crx)
624 continue;
625
626 if (crx->status != CrxUpdateItem::kChecking)
627 continue; // Not updating this component now.
628
[email protected]360b8bb2011-09-01 21:48:06629 config_->OnEvent(Configurator::kManifestCheck, CrxIdtoUMAId(crx->id));
630
[email protected]e8f96ff2011-08-03 05:07:33631 if (it->version.empty()) {
[email protected]cf442612011-08-09 20:20:12632 // No version means no update available.
[email protected]e8f96ff2011-08-03 05:07:33633 crx->status = CrxUpdateItem::kNoUpdate;
[email protected]cf442612011-08-09 20:20:12634 continue;
[email protected]e8f96ff2011-08-03 05:07:33635 }
636 if (!IsVersionNewer(crx->component.version, it->version)) {
[email protected]cf442612011-08-09 20:20:12637 // Our component is up to date.
[email protected]e8f96ff2011-08-03 05:07:33638 crx->status = CrxUpdateItem::kUpToDate;
[email protected]cf442612011-08-09 20:20:12639 continue;
[email protected]e8f96ff2011-08-03 05:07:33640 }
641 if (!it->browser_min_version.empty()) {
[email protected]cf442612011-08-09 20:20:12642 if (IsVersionNewer(chrome_version_, it->browser_min_version)) {
643 // Does not apply for this chrome version.
644 crx->status = CrxUpdateItem::kNoUpdate;
645 continue;
646 }
[email protected]e8f96ff2011-08-03 05:07:33647 }
648 // All test passed. Queue an upgrade for this component and fire the
649 // notifications.
650 crx->crx_url = it->crx_url;
651 crx->status = CrxUpdateItem::kCanUpdate;
[email protected]07f93af12011-08-17 20:57:22652 crx->next_version = Version(it->version);
[email protected]e8f96ff2011-08-03 05:07:33653 ++update_pending;
654
[email protected]ad50def52011-10-19 23:17:07655 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33656 chrome::NOTIFICATION_COMPONENT_UPDATE_FOUND,
[email protected]6c2381d2011-10-19 02:52:53657 content::Source<std::string>(&crx->id),
[email protected]ad50def52011-10-19 23:17:07658 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33659 }
[email protected]cf442612011-08-09 20:20:12660
661 // All the components that are not mentioned in the manifest we
662 // consider them up to date.
663 ChangeItemStatus(CrxUpdateItem::kChecking, CrxUpdateItem::kUpToDate);
664
[email protected]e8f96ff2011-08-03 05:07:33665 // If there are updates pending we do a short wait.
666 ScheduleNextRun(update_pending ? true : false);
667}
668
669void CrxUpdateService::OnParseUpdateManifestFailed(
670 const std::string& error_message) {
671 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
672 size_t count = ChangeItemStatus(CrxUpdateItem::kChecking,
673 CrxUpdateItem::kNoUpdate);
[email protected]360b8bb2011-09-01 21:48:06674 config_->OnEvent(Configurator::kManifestError, static_cast<int>(count));
[email protected]e8f96ff2011-08-03 05:07:33675 DCHECK_GT(count, 0ul);
676 ScheduleNextRun(false);
677}
678
679// Called when the CRX package has been downloaded to a temporary location.
680// Here we fire the notifications and schedule the component-specific installer
681// to be called in the file thread.
[email protected]7cc6e5632011-10-25 17:56:12682void CrxUpdateService::OnURLFetchComplete(const content::URLFetcher* source,
[email protected]e8f96ff2011-08-03 05:07:33683 CRXContext* context) {
684 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
685 base::PlatformFileError error_code;
686
687 if (source->FileErrorOccurred(&error_code) || !FetchSuccess(*source)) {
688 size_t count = ChangeItemStatus(CrxUpdateItem::kDownloading,
689 CrxUpdateItem::kNoUpdate);
690 DCHECK_EQ(count, 1ul);
[email protected]360b8bb2011-09-01 21:48:06691 config_->OnEvent(Configurator::kNetworkError, CrxIdtoUMAId(context->id));
[email protected]cf442612011-08-09 20:20:12692 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33693 ScheduleNextRun(false);
694 } else {
695 FilePath temp_crx_path;
696 CHECK(source->GetResponseAsFilePath(true, &temp_crx_path));
697 size_t count = ChangeItemStatus(CrxUpdateItem::kDownloading,
698 CrxUpdateItem::kUpdating);
699 DCHECK_EQ(count, 1ul);
[email protected]cf442612011-08-09 20:20:12700 url_fetcher_.reset();
701
[email protected]ad50def52011-10-19 23:17:07702 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33703 chrome::NOTIFICATION_COMPONENT_UPDATE_READY,
[email protected]6c2381d2011-10-19 02:52:53704 content::Source<std::string>(&context->id),
[email protected]ad50def52011-10-19 23:17:07705 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33706
[email protected]44da56e2011-11-21 19:59:14707 // Why unretained? See comment at top of file.
[email protected]73251e72012-03-04 02:10:33708 BrowserThread::PostDelayedTask(
709 BrowserThread::FILE,
710 FROM_HERE,
[email protected]44da56e2011-11-21 19:59:14711 base::Bind(&CrxUpdateService::Install,
712 base::Unretained(this),
713 context,
714 temp_crx_path),
[email protected]73251e72012-03-04 02:10:33715 base::TimeDelta::FromMilliseconds(config_->StepDelay()));
[email protected]e8f96ff2011-08-03 05:07:33716 }
[email protected]e8f96ff2011-08-03 05:07:33717}
718
719// Install consists of digital signature verification, unpacking and then
720// calling the component specific installer. All that is handled by the
721// |unpacker|. If there is an error this function is in charge of deleting
722// the files created.
723void CrxUpdateService::Install(const CRXContext* context,
724 const FilePath& crx_path) {
725 // This function owns the |crx_path| and the |context| object.
726 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
727 ComponentUnpacker
728 unpacker(context->pk_hash, crx_path, context->installer);
[email protected]e8f96ff2011-08-03 05:07:33729 if (!file_util::Delete(crx_path, false)) {
730 NOTREACHED() << crx_path.value();
731 }
[email protected]44da56e2011-11-21 19:59:14732 // Why unretained? See comment at top of file.
[email protected]73251e72012-03-04 02:10:33733 BrowserThread::PostDelayedTask(
734 BrowserThread::UI,
735 FROM_HERE,
[email protected]44da56e2011-11-21 19:59:14736 base::Bind(&CrxUpdateService::DoneInstalling, base::Unretained(this),
737 context->id, unpacker.error()),
[email protected]73251e72012-03-04 02:10:33738 base::TimeDelta::FromMilliseconds(config_->StepDelay()));
[email protected]07f93af12011-08-17 20:57:22739 delete context;
[email protected]e8f96ff2011-08-03 05:07:33740}
741
742// Installation has been completed. Adjust the component status and
743// schedule the next check.
[email protected]07f93af12011-08-17 20:57:22744void CrxUpdateService::DoneInstalling(const std::string& component_id,
745 ComponentUnpacker::Error error) {
[email protected]e8f96ff2011-08-03 05:07:33746 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]07f93af12011-08-17 20:57:22747
748 CrxUpdateItem* item = FindUpdateItemById(component_id);
749 item->status = (error == ComponentUnpacker::kNone) ? CrxUpdateItem::kUpdated :
750 CrxUpdateItem::kNoUpdate;
751 if (item->status == CrxUpdateItem::kUpdated)
752 item->component.version = item->next_version;
753
[email protected]360b8bb2011-09-01 21:48:06754 Configurator::Events event;
755 switch (error) {
756 case ComponentUnpacker::kNone:
757 event = Configurator::kComponentUpdated;
758 break;
759 case ComponentUnpacker::kInstallerError:
760 event = Configurator::kInstallerError;
761 break;
762 default:
763 event = Configurator::kUnpackError;
764 break;
765 }
766
767 config_->OnEvent(event, CrxIdtoUMAId(component_id));
[email protected]e8f96ff2011-08-03 05:07:33768 ScheduleNextRun(false);
769}
770
771// The component update factory. Using the component updater as a singleton
772// is the job of the browser process.
773ComponentUpdateService* ComponentUpdateServiceFactory(
774 ComponentUpdateService::Configurator* config) {
775 DCHECK(config);
776 return new CrxUpdateService(config);
777}