blob: 0c090cce9b9c7e6bc578ffd837395a09b899a334 [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]f1050432012-02-15 01:35:46263 // Omaha update response XML was successfully parsed.
[email protected]e8f96ff2011-08-03 05:07:33264 void OnParseUpdateManifestSucceeded(const UpdateManifest::Results& r) {
265 service_->OnParseUpdateManifestSucceeded(r);
266 }
267 // Omaha update response XML could not be parsed.
268 void OnParseUpdateManifestFailed(const std::string& e) {
269 service_->OnParseUpdateManifestFailed(e);
270 }
271
272 CrxUpdateService* service_;
273 DISALLOW_COPY_AND_ASSIGN(ManifestParserBridge);
274 };
275
276 // Context for a update check url request. See DelegateWithContext above.
277 struct UpdateContext {
278 base::Time start;
279 UpdateContext() : start(base::Time::Now()) {}
280 };
281
282 // Context for a crx download url request. See DelegateWithContext above.
283 struct CRXContext {
284 ComponentInstaller* installer;
285 std::vector<uint8> pk_hash;
286 std::string id;
287 CRXContext() : installer(NULL) {}
288 };
289
[email protected]7cc6e5632011-10-25 17:56:12290 void OnURLFetchComplete(const content::URLFetcher* source,
291 UpdateContext* context);
[email protected]e8f96ff2011-08-03 05:07:33292
[email protected]7cc6e5632011-10-25 17:56:12293 void OnURLFetchComplete(const content::URLFetcher* source,
294 CRXContext* context);
[email protected]e8f96ff2011-08-03 05:07:33295
296 private:
297 // See ManifestParserBridge.
298 void OnParseUpdateManifestSucceeded(
299 const UpdateManifest::Results& results);
300
301 // See ManifestParserBridge.
302 void OnParseUpdateManifestFailed(
303 const std::string& error_message);
304
305 bool AddItemToUpdateCheck(CrxUpdateItem* item, std::string* query);
306
307 void ProcessPendingItems();
308
309 void ScheduleNextRun(bool step_delay);
310
311 void ParseManifest(const std::string& xml);
312
313 void Install(const CRXContext* context, const FilePath& crx_path);
314
[email protected]07f93af12011-08-17 20:57:22315 void DoneInstalling(const std::string& component_id,
316 ComponentUnpacker::Error error);
[email protected]e8f96ff2011-08-03 05:07:33317
318 size_t ChangeItemStatus(CrxUpdateItem::Status from,
319 CrxUpdateItem::Status to);
320
321 CrxUpdateItem* FindUpdateItemById(const std::string& id);
322
323 scoped_ptr<Config> config_;
324
[email protected]7cc6e5632011-10-25 17:56:12325 scoped_ptr<content::URLFetcher> url_fetcher_;
[email protected]e8f96ff2011-08-03 05:07:33326
327 typedef std::vector<CrxUpdateItem*> UpdateItems;
328 UpdateItems work_items_;
329
330 base::OneShotTimer<CrxUpdateService> timer_;
331
332 Version chrome_version_;
333
334 bool running_;
335
336 DISALLOW_COPY_AND_ASSIGN(CrxUpdateService);
337};
338
[email protected]e8f96ff2011-08-03 05:07:33339//////////////////////////////////////////////////////////////////////////////
340
341CrxUpdateService::CrxUpdateService(
342 ComponentUpdateService::Configurator* config)
343 : config_(config),
344 chrome_version_(chrome::VersionInfo().Version()),
345 running_(false) {
346}
347
348CrxUpdateService::~CrxUpdateService() {
349 // Because we are a singleton, at this point only the UI thread should be
350 // alive, this simplifies the management of the work that could be in
351 // flight in other threads.
352 Stop();
353 STLDeleteElements(&work_items_);
354}
355
356ComponentUpdateService::Status CrxUpdateService::Start() {
357 // Note that RegisterComponent will call Start() when the first
358 // component is registered, so it can be called twice. This way
359 // we avoid scheduling the timer if there is no work to do.
360 running_ = true;
361 if (work_items_.empty())
362 return kOk;
363
[email protected]ad50def52011-10-19 23:17:07364 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33365 chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED,
[email protected]6c2381d2011-10-19 02:52:53366 content::Source<ComponentUpdateService>(this),
[email protected]ad50def52011-10-19 23:17:07367 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33368
[email protected]d323a172011-09-02 18:23:02369 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(config_->InitialDelay()),
[email protected]e8f96ff2011-08-03 05:07:33370 this, &CrxUpdateService::ProcessPendingItems);
371 return kOk;
372}
373
374// Stop the main check + update loop. In flight operations will be
375// completed.
376ComponentUpdateService::Status CrxUpdateService::Stop() {
377 running_ = false;
378 timer_.Stop();
379 return kOk;
380}
381
382// This function sets the timer which will call ProcessPendingItems() there
383// are two kind of waits, the short one (with step_delay = true) and the
384// long one.
385void CrxUpdateService::ScheduleNextRun(bool step_delay) {
386 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]cf442612011-08-09 20:20:12387 DCHECK(url_fetcher_.get() == NULL);
[email protected]e8f96ff2011-08-03 05:07:33388 CHECK(!timer_.IsRunning());
389 // It could be the case that Stop() had been called while a url request
390 // or unpacking was in flight, if so we arrive here but |running_| is
391 // false. In that case do not loop again.
392 if (!running_)
393 return;
394
[email protected]cf442612011-08-09 20:20:12395 int64 delay = step_delay ? config_->StepDelay() : config_->NextCheckDelay();
396
[email protected]e8f96ff2011-08-03 05:07:33397 if (!step_delay) {
[email protected]ad50def52011-10-19 23:17:07398 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33399 chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING,
[email protected]6c2381d2011-10-19 02:52:53400 content::Source<ComponentUpdateService>(this),
[email protected]ad50def52011-10-19 23:17:07401 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33402 // Zero is only used for unit tests.
[email protected]cf442612011-08-09 20:20:12403 if (0 == delay)
[email protected]e8f96ff2011-08-03 05:07:33404 return;
405 }
406
[email protected]d323a172011-09-02 18:23:02407 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(delay),
[email protected]e8f96ff2011-08-03 05:07:33408 this, &CrxUpdateService::ProcessPendingItems);
409}
410
411// Given a extension-like component id, find the associated component.
412CrxUpdateItem* CrxUpdateService::FindUpdateItemById(const std::string& id) {
413 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
414 CrxUpdateItem::FindById finder(id);
415 UpdateItems::iterator it = std::find_if(work_items_.begin(),
416 work_items_.end(),
417 finder);
418 if (it == work_items_.end())
419 return NULL;
420 return (*it);
421}
422
423// Changes all the components in |work_items_| that have |from| status to
424// |to| statatus and returns how many have been changed.
425size_t CrxUpdateService::ChangeItemStatus(CrxUpdateItem::Status from,
426 CrxUpdateItem::Status to) {
427 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
428 size_t count = 0;
429 for (UpdateItems::iterator it = work_items_.begin();
430 it != work_items_.end(); ++it) {
431 CrxUpdateItem* item = *it;
432 if (item->status != from)
433 continue;
434 item->status = to;
435 ++count;
436 }
437 return count;
438}
439
440// Adds a component to be checked for upgrades. If the component exists it
441// it will be replaced and the return code is kReplaced.
442//
443// TODO(cpu): Evaluate if we want to support un-registration.
444ComponentUpdateService::Status CrxUpdateService::RegisterComponent(
445 const CrxComponent& component) {
446 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
447 if (component.pk_hash.empty() ||
448 !component.version.IsValid() ||
449 !component.installer)
450 return kError;
451
452 std::string id =
453 HexStringToID(StringToLowerASCII(base::HexEncode(&component.pk_hash[0],
454 component.pk_hash.size()/2)));
455 CrxUpdateItem* uit;
456 uit = FindUpdateItemById(id);
457 if (uit) {
458 uit->component = component;
459 return kReplaced;
460 }
461
462 uit = new CrxUpdateItem;
463 uit->id.swap(id);
464 uit->component = component;
465 work_items_.push_back(uit);
466 // If this is the first component registered we call Start to
467 // schedule the first timer.
468 if (running_ && (work_items_.size() == 1))
469 Start();
470
471 return kOk;
472}
473
474// Sets a component to be checked for updates.
475// The componet to add is |crxit| and the |query| string is modified with the
476// required omaha compatible query. Returns false when the query strings
477// is longer than specified by UrlSizeLimit().
478bool CrxUpdateService::AddItemToUpdateCheck(CrxUpdateItem* item,
479 std::string* query) {
480 if (!AddQueryString(item->id,
481 item->component.version.GetString(),
482 config_->UrlSizeLimit(), query))
483 return false;
484 item->status = CrxUpdateItem::kChecking;
485 item->last_check = base::Time::Now();
486 return true;
487}
488
489// Here is where the work gets scheduled. Given that our |work_items_| list
490// is expected to be ten or less items, we simply loop several times.
491void CrxUpdateService::ProcessPendingItems() {
492 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
493 // First check for ready upgrades and do one. The first
494 // step is to fetch the crx package.
495 for (UpdateItems::const_iterator it = work_items_.begin();
496 it != work_items_.end(); ++it) {
497 CrxUpdateItem* item = *it;
498 if (item->status != CrxUpdateItem::kCanUpdate)
499 continue;
500 // Found component to update, start the process.
501 item->status = CrxUpdateItem::kDownloading;
502 CRXContext* context = new CRXContext;
503 context->pk_hash = item->component.pk_hash;
504 context->id = item->id;
505 context->installer = item->component.installer;
[email protected]36aea2702011-10-26 01:12:22506 url_fetcher_.reset(content::URLFetcher::Create(
507 0, item->crx_url, content::URLFetcher::GET,
[email protected]e8f96ff2011-08-03 05:07:33508 MakeContextDelegate(this, context)));
509 StartFetch(url_fetcher_.get(), config_->RequestContext(), true);
510 return;
511 }
512
513 std::string query;
514 // If no pending upgrades, we check the if there are new
515 // components we have not checked against the server. We
516 // can batch a bunch in a single url request.
517 for (UpdateItems::const_iterator it = work_items_.begin();
518 it != work_items_.end(); ++it) {
519 CrxUpdateItem* item = *it;
520 if (item->status != CrxUpdateItem::kNew)
521 continue;
522 if (!AddItemToUpdateCheck(item, &query))
523 break;
524 }
525
526 // Next we can go back to components we already checked, here
527 // we can also batch them in a single url request, as long as
528 // we have not checked them recently.
529 base::TimeDelta min_delta_time =
530 base::TimeDelta::FromSeconds(config_->MinimumReCheckWait());
531
532 for (UpdateItems::const_iterator it = work_items_.begin();
533 it != work_items_.end(); ++it) {
534 CrxUpdateItem* item = *it;
[email protected]cf442612011-08-09 20:20:12535 if ((item->status != CrxUpdateItem::kNoUpdate) &&
[email protected]e8f96ff2011-08-03 05:07:33536 (item->status != CrxUpdateItem::kUpToDate))
537 continue;
538 base::TimeDelta delta = base::Time::Now() - item->last_check;
539 if (delta < min_delta_time)
540 continue;
541 if (!AddItemToUpdateCheck(item, &query))
542 break;
543 }
544 // Finally, we check components that we already updated.
545 for (UpdateItems::const_iterator it = work_items_.begin();
546 it != work_items_.end(); ++it) {
547 CrxUpdateItem* item = *it;
548 if (item->status != CrxUpdateItem::kUpdated)
549 continue;
550 base::TimeDelta delta = base::Time::Now() - item->last_check;
551 if (delta < min_delta_time)
552 continue;
553 if (!AddItemToUpdateCheck(item, &query))
554 break;
555 }
556
557 if (query.empty()) {
558 // Next check after the long sleep.
559 ScheduleNextRun(false);
560 return;
561 }
562
563 // We got components to check. Start the url request.
[email protected]926d36332011-10-05 01:06:25564 const std::string full_query = MakeFinalQuery(config_->UpdateUrl().spec(),
565 query,
566 config_->ExtraRequestParams());
[email protected]36aea2702011-10-26 01:12:22567 url_fetcher_.reset(content::URLFetcher::Create(
568 0, GURL(full_query), content::URLFetcher::GET,
[email protected]e8f96ff2011-08-03 05:07:33569 MakeContextDelegate(this, new UpdateContext())));
570 StartFetch(url_fetcher_.get(), config_->RequestContext(), false);
571}
572
573// Caled when we got a response from the update server. It consists of an xml
574// document following the omaha update scheme.
[email protected]7cc6e5632011-10-25 17:56:12575void CrxUpdateService::OnURLFetchComplete(const content::URLFetcher* source,
[email protected]07f93af12011-08-17 20:57:22576 UpdateContext* context) {
[email protected]e8f96ff2011-08-03 05:07:33577 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
578 if (FetchSuccess(*source)) {
579 std::string xml;
580 source->GetResponseAsString(&xml);
[email protected]cf442612011-08-09 20:20:12581 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33582 ParseManifest(xml);
583 } else {
[email protected]cf442612011-08-09 20:20:12584 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33585 CrxUpdateService::OnParseUpdateManifestFailed("network error");
586 }
[email protected]07f93af12011-08-17 20:57:22587 delete context;
[email protected]e8f96ff2011-08-03 05:07:33588}
589
590// Parsing the manifest is either done right now for tests or in a sandboxed
591// process for the production environment. This mitigates the case where an
592// attacker was able to feed us a malicious xml string.
593void CrxUpdateService::ParseManifest(const std::string& xml) {
594 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
595 if (config_->InProcess()) {
596 UpdateManifest manifest;
597 if (!manifest.Parse(xml)) {
598 CrxUpdateService::OnParseUpdateManifestFailed(manifest.errors());
599 } else {
600 CrxUpdateService::OnParseUpdateManifestSucceeded(manifest.results());
601 }
602 } else {
[email protected]c4f883a2012-02-03 17:02:07603 UtilityProcessHost* host = UtilityProcessHost::Create(
604 new ManifestParserBridge(this), BrowserThread::UI);
605 host->EnableZygote();
[email protected]2ccf45c2011-08-19 23:35:50606 host->Send(new ChromeUtilityMsg_ParseUpdateManifest(xml));
[email protected]e8f96ff2011-08-03 05:07:33607 }
608}
609
610// A valid Omaha update check has arrived, from only the list of components that
611// we are currently upgrading we check for a match in which the server side
612// version is newer, if so we queue them for an upgrade. The next time we call
613// ProcessPendingItems() one of them will be drafted for the upgrade process.
614void CrxUpdateService::OnParseUpdateManifestSucceeded(
615 const UpdateManifest::Results& results) {
616 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
617 int update_pending = 0;
618 std::vector<UpdateManifest::Result>::const_iterator it;
619 for (it = results.list.begin(); it != results.list.end(); ++it) {
620 CrxUpdateItem* crx = FindUpdateItemById(it->extension_id);
621 if (!crx)
622 continue;
623
624 if (crx->status != CrxUpdateItem::kChecking)
625 continue; // Not updating this component now.
626
[email protected]360b8bb2011-09-01 21:48:06627 config_->OnEvent(Configurator::kManifestCheck, CrxIdtoUMAId(crx->id));
628
[email protected]e8f96ff2011-08-03 05:07:33629 if (it->version.empty()) {
[email protected]cf442612011-08-09 20:20:12630 // No version means no update available.
[email protected]e8f96ff2011-08-03 05:07:33631 crx->status = CrxUpdateItem::kNoUpdate;
[email protected]cf442612011-08-09 20:20:12632 continue;
[email protected]e8f96ff2011-08-03 05:07:33633 }
634 if (!IsVersionNewer(crx->component.version, it->version)) {
[email protected]cf442612011-08-09 20:20:12635 // Our component is up to date.
[email protected]e8f96ff2011-08-03 05:07:33636 crx->status = CrxUpdateItem::kUpToDate;
[email protected]cf442612011-08-09 20:20:12637 continue;
[email protected]e8f96ff2011-08-03 05:07:33638 }
639 if (!it->browser_min_version.empty()) {
[email protected]cf442612011-08-09 20:20:12640 if (IsVersionNewer(chrome_version_, it->browser_min_version)) {
641 // Does not apply for this chrome version.
642 crx->status = CrxUpdateItem::kNoUpdate;
643 continue;
644 }
[email protected]e8f96ff2011-08-03 05:07:33645 }
646 // All test passed. Queue an upgrade for this component and fire the
647 // notifications.
648 crx->crx_url = it->crx_url;
649 crx->status = CrxUpdateItem::kCanUpdate;
[email protected]07f93af12011-08-17 20:57:22650 crx->next_version = Version(it->version);
[email protected]e8f96ff2011-08-03 05:07:33651 ++update_pending;
652
[email protected]ad50def52011-10-19 23:17:07653 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33654 chrome::NOTIFICATION_COMPONENT_UPDATE_FOUND,
[email protected]6c2381d2011-10-19 02:52:53655 content::Source<std::string>(&crx->id),
[email protected]ad50def52011-10-19 23:17:07656 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33657 }
[email protected]cf442612011-08-09 20:20:12658
659 // All the components that are not mentioned in the manifest we
660 // consider them up to date.
661 ChangeItemStatus(CrxUpdateItem::kChecking, CrxUpdateItem::kUpToDate);
662
[email protected]e8f96ff2011-08-03 05:07:33663 // If there are updates pending we do a short wait.
664 ScheduleNextRun(update_pending ? true : false);
665}
666
667void CrxUpdateService::OnParseUpdateManifestFailed(
668 const std::string& error_message) {
669 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
670 size_t count = ChangeItemStatus(CrxUpdateItem::kChecking,
671 CrxUpdateItem::kNoUpdate);
[email protected]360b8bb2011-09-01 21:48:06672 config_->OnEvent(Configurator::kManifestError, static_cast<int>(count));
[email protected]e8f96ff2011-08-03 05:07:33673 DCHECK_GT(count, 0ul);
674 ScheduleNextRun(false);
675}
676
677// Called when the CRX package has been downloaded to a temporary location.
678// Here we fire the notifications and schedule the component-specific installer
679// to be called in the file thread.
[email protected]7cc6e5632011-10-25 17:56:12680void CrxUpdateService::OnURLFetchComplete(const content::URLFetcher* source,
[email protected]e8f96ff2011-08-03 05:07:33681 CRXContext* context) {
682 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
683 base::PlatformFileError error_code;
684
685 if (source->FileErrorOccurred(&error_code) || !FetchSuccess(*source)) {
686 size_t count = ChangeItemStatus(CrxUpdateItem::kDownloading,
687 CrxUpdateItem::kNoUpdate);
688 DCHECK_EQ(count, 1ul);
[email protected]360b8bb2011-09-01 21:48:06689 config_->OnEvent(Configurator::kNetworkError, CrxIdtoUMAId(context->id));
[email protected]cf442612011-08-09 20:20:12690 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33691 ScheduleNextRun(false);
692 } else {
693 FilePath temp_crx_path;
694 CHECK(source->GetResponseAsFilePath(true, &temp_crx_path));
695 size_t count = ChangeItemStatus(CrxUpdateItem::kDownloading,
696 CrxUpdateItem::kUpdating);
697 DCHECK_EQ(count, 1ul);
[email protected]cf442612011-08-09 20:20:12698 url_fetcher_.reset();
699
[email protected]ad50def52011-10-19 23:17:07700 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33701 chrome::NOTIFICATION_COMPONENT_UPDATE_READY,
[email protected]6c2381d2011-10-19 02:52:53702 content::Source<std::string>(&context->id),
[email protected]ad50def52011-10-19 23:17:07703 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33704
[email protected]44da56e2011-11-21 19:59:14705 // Why unretained? See comment at top of file.
[email protected]e8f96ff2011-08-03 05:07:33706 BrowserThread::PostDelayedTask(BrowserThread::FILE, FROM_HERE,
[email protected]44da56e2011-11-21 19:59:14707 base::Bind(&CrxUpdateService::Install,
708 base::Unretained(this),
709 context,
710 temp_crx_path),
[email protected]e8f96ff2011-08-03 05:07:33711 config_->StepDelay());
712 }
[email protected]e8f96ff2011-08-03 05:07:33713}
714
715// Install consists of digital signature verification, unpacking and then
716// calling the component specific installer. All that is handled by the
717// |unpacker|. If there is an error this function is in charge of deleting
718// the files created.
719void CrxUpdateService::Install(const CRXContext* context,
720 const FilePath& crx_path) {
721 // This function owns the |crx_path| and the |context| object.
722 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
723 ComponentUnpacker
724 unpacker(context->pk_hash, crx_path, context->installer);
[email protected]e8f96ff2011-08-03 05:07:33725 if (!file_util::Delete(crx_path, false)) {
726 NOTREACHED() << crx_path.value();
727 }
[email protected]44da56e2011-11-21 19:59:14728 // Why unretained? See comment at top of file.
[email protected]e8f96ff2011-08-03 05:07:33729 BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE,
[email protected]44da56e2011-11-21 19:59:14730 base::Bind(&CrxUpdateService::DoneInstalling, base::Unretained(this),
731 context->id, unpacker.error()),
[email protected]e8f96ff2011-08-03 05:07:33732 config_->StepDelay());
[email protected]07f93af12011-08-17 20:57:22733 delete context;
[email protected]e8f96ff2011-08-03 05:07:33734}
735
736// Installation has been completed. Adjust the component status and
737// schedule the next check.
[email protected]07f93af12011-08-17 20:57:22738void CrxUpdateService::DoneInstalling(const std::string& component_id,
739 ComponentUnpacker::Error error) {
[email protected]e8f96ff2011-08-03 05:07:33740 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]07f93af12011-08-17 20:57:22741
742 CrxUpdateItem* item = FindUpdateItemById(component_id);
743 item->status = (error == ComponentUnpacker::kNone) ? CrxUpdateItem::kUpdated :
744 CrxUpdateItem::kNoUpdate;
745 if (item->status == CrxUpdateItem::kUpdated)
746 item->component.version = item->next_version;
747
[email protected]360b8bb2011-09-01 21:48:06748 Configurator::Events event;
749 switch (error) {
750 case ComponentUnpacker::kNone:
751 event = Configurator::kComponentUpdated;
752 break;
753 case ComponentUnpacker::kInstallerError:
754 event = Configurator::kInstallerError;
755 break;
756 default:
757 event = Configurator::kUnpackError;
758 break;
759 }
760
761 config_->OnEvent(event, CrxIdtoUMAId(component_id));
[email protected]e8f96ff2011-08-03 05:07:33762 ScheduleNextRun(false);
763}
764
765// The component update factory. Using the component updater as a singleton
766// is the job of the browser process.
767ComponentUpdateService* ComponentUpdateServiceFactory(
768 ComponentUpdateService::Configurator* config) {
769 DCHECK(config);
770 return new CrxUpdateService(config);
771}