blob: f1bdaa838e2d21559d42a2d64b761e018423888a [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]36aea2702011-10-26 01:12:2231#include "content/public/common/url_fetcher.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]15fb2aa2012-05-22 22:52:5935#include "net/url_request/url_fetcher_delegate.h"
[email protected]e8f96ff2011-08-03 05:07:3336
[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]1c321ee52012-05-21 03:02:3440using extensions::Extension;
[email protected]631bb742011-11-02 11:29:3941
[email protected]44da56e2011-11-21 19:59:1442// The component updater is designed to live until process shutdown, so
43// base::Bind() calls are not refcounted.
44
[email protected]e8f96ff2011-08-03 05:07:3345namespace {
46// Extends an omaha compatible update check url |query| string. Does
47// not mutate the string if it would be longer than |limit| chars.
[email protected]4c37b452011-12-21 01:33:5248bool AddQueryString(const std::string& id,
49 const std::string& version,
50 size_t limit,
51 std::string* query) {
[email protected]e8f96ff2011-08-03 05:07:3352 std::string additional =
53 base::StringPrintf("id=%s&v=%s&uc", id.c_str(), version.c_str());
[email protected]4a19be92011-09-22 14:25:0254 additional = "x=" + net::EscapeQueryParamValue(additional, true);
[email protected]e8f96ff2011-08-03 05:07:3355 if ((additional.size() + query->size() + 1) > limit)
56 return false;
[email protected]926d36332011-10-05 01:06:2557 if (!query->empty())
58 query->append(1, '&');
[email protected]e8f96ff2011-08-03 05:07:3359 query->append(additional);
60 return true;
61}
62
[email protected]926d36332011-10-05 01:06:2563// Create the final omaha compatible query. The |extra| is optional and can
64// be null. It should contain top level (non-escaped) parameters.
65std::string MakeFinalQuery(const std::string& host,
66 const std::string& query,
67 const char* extra) {
68 std::string request(host);
69 request.append(1, '?');
70 if (extra) {
71 request.append(extra);
72 request.append(1, '&');
73 }
74 request.append(query);
75 return request;
76}
77
[email protected]e8f96ff2011-08-03 05:07:3378// Produces an extension-like friendly |id|. This might be removed in the
79// future if we roll our on packing tools.
80static std::string HexStringToID(const std::string& hexstr) {
81 std::string id;
82 for (size_t i = 0; i < hexstr.size(); ++i) {
83 int val;
[email protected]eb72b272011-12-19 16:10:5584 if (base::HexStringToInt(base::StringPiece(hexstr.begin() + i,
85 hexstr.begin() + i + 1),
86 &val)) {
[email protected]e8f96ff2011-08-03 05:07:3387 id.append(1, val + 'a');
[email protected]eb72b272011-12-19 16:10:5588 } else {
[email protected]e8f96ff2011-08-03 05:07:3389 id.append(1, 'a');
[email protected]eb72b272011-12-19 16:10:5590 }
[email protected]e8f96ff2011-08-03 05:07:3391 }
92 DCHECK(Extension::IdIsValid(id));
93 return id;
94}
95
[email protected]360b8bb2011-09-01 21:48:0696// Returns given a crx id it returns a small number, less than 100, that has a
97// decent chance of being unique among the registered components. It also has
98// the nice property that can be trivially computed by hand.
99static int CrxIdtoUMAId(const std::string& id) {
100 CHECK(id.size() > 2);
101 return id[0] + id[1] + id[2] - ('a' * 3);
102}
103
[email protected]e8f96ff2011-08-03 05:07:33104// Helper to do version check for components.
105bool IsVersionNewer(const Version& current, const std::string& proposed) {
106 Version proposed_ver(proposed);
107 if (!proposed_ver.IsValid())
108 return false;
109 return (current.CompareTo(proposed_ver) < 0);
110}
111
112// Helper template class that allows our main class to have separate
[email protected]f1050432012-02-15 01:35:46113// OnURLFetchComplete() callbacks for different types of url requests
[email protected]e8f96ff2011-08-03 05:07:33114// they are differentiated by the |Ctx| type.
115template <typename Del, typename Ctx>
[email protected]15fb2aa2012-05-22 22:52:59116class DelegateWithContext : public net::URLFetcherDelegate {
[email protected]e8f96ff2011-08-03 05:07:33117 public:
118 DelegateWithContext(Del* delegate, Ctx* context)
119 : delegate_(delegate), context_(context) {}
120
[email protected]10c2d692012-05-11 05:32:23121 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
[email protected]e8f96ff2011-08-03 05:07:33122 delegate_->OnURLFetchComplete(source, context_);
123 delete this;
124 }
125
126 private:
127 ~DelegateWithContext() {}
128
129 Del* delegate_;
130 Ctx* context_;
131};
132// This function creates the right DelegateWithContext using template inference.
133template <typename Del, typename Ctx>
[email protected]15fb2aa2012-05-22 22:52:59134net::URLFetcherDelegate* MakeContextDelegate(Del* delegate, Ctx* context) {
[email protected]e8f96ff2011-08-03 05:07:33135 return new DelegateWithContext<Del, Ctx>(delegate, context);
136}
137
138// Helper to start a url request using |fetcher| with the common flags.
[email protected]7cc6e5632011-10-25 17:56:12139void StartFetch(content::URLFetcher* fetcher,
[email protected]e8f96ff2011-08-03 05:07:33140 net::URLRequestContextGetter* context_getter,
141 bool save_to_file) {
[email protected]7cc6e5632011-10-25 17:56:12142 fetcher->SetRequestContext(context_getter);
143 fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
144 net::LOAD_DO_NOT_SAVE_COOKIES |
145 net::LOAD_DISABLE_CACHE);
[email protected]e8f96ff2011-08-03 05:07:33146 // TODO(cpu): Define our retry and backoff policy.
[email protected]7cc6e5632011-10-25 17:56:12147 fetcher->SetAutomaticallyRetryOn5xx(false);
[email protected]e8f96ff2011-08-03 05:07:33148 if (save_to_file) {
149 fetcher->SaveResponseToTemporaryFile(
150 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
151 }
152 fetcher->Start();
153}
154
155// Returs true if the url request of |fetcher| was succesful.
[email protected]10c2d692012-05-11 05:32:23156bool FetchSuccess(const net::URLFetcher& fetcher) {
[email protected]7cc6e5632011-10-25 17:56:12157 return (fetcher.GetStatus().status() == net::URLRequestStatus::SUCCESS) &&
158 (fetcher.GetResponseCode() == 200);
[email protected]e8f96ff2011-08-03 05:07:33159}
160
161// This is the one and only per-item state structure. Designed to be hosted
162// in a std::vector or a std::list. The two main members are |component|
163// which is supplied by the the component updater client and |status| which
164// is modified as the item is processed by the update pipeline. The expected
165// transition graph is:
166// error error error
167// +--kNoUpdate<------<-------+------<------+------<------+
168// | | | |
169// V yes | | |
170// kNew --->kChecking-->[update?]----->kCanUpdate-->kDownloading-->kUpdating
171// ^ | |
172// | |no |
173// |--kUpToDate<---+ |
174// | success |
175// +--kUpdated<-------------------------------------------+
176//
177struct CrxUpdateItem {
178 enum Status {
179 kNew,
180 kChecking,
181 kCanUpdate,
182 kDownloading,
183 kUpdating,
184 kUpdated,
185 kUpToDate,
186 kNoUpdate,
187 kLastStatus
188 };
189
190 Status status;
191 GURL crx_url;
192 std::string id;
193 base::Time last_check;
194 CrxComponent component;
[email protected]07f93af12011-08-17 20:57:22195 Version next_version;
[email protected]e8f96ff2011-08-03 05:07:33196
197 CrxUpdateItem() : status(kNew) {}
198
199 // Function object used to find a specific component.
200 class FindById {
201 public:
202 explicit FindById(const std::string& id) : id_(id) {}
203
204 bool operator() (CrxUpdateItem* item) const {
205 return (item->id == id_);
206 }
207 private:
208 const std::string& id_;
209 };
210};
211
212} // namespace.
213
214typedef ComponentUpdateService::Configurator Config;
215
[email protected]1e545b62012-01-24 17:01:10216CrxComponent::CrxComponent() : installer(NULL) {}
[email protected]e8f96ff2011-08-03 05:07:33217CrxComponent::~CrxComponent() {}
218
219//////////////////////////////////////////////////////////////////////////////
220// The one and only implementation of the ComponentUpdateService interface. In
221// charge of running the show. The main method is ProcessPendingItems() which
222// is called periodically to do the updgrades/installs or the update checks.
223// An important consideration here is to be as "low impact" as we can to the
224// rest of the browser, so even if we have many components registered and
[email protected]f1050432012-02-15 01:35:46225// eligible for update, we only do one thing at a time with pauses in between
[email protected]e8f96ff2011-08-03 05:07:33226// the tasks. Also when we do network requests there is only one |url_fetcher_|
227// in flight at at a time.
228// There are no locks in this code, the main structure |work_items_| is mutated
229// only from the UI thread. The unpack and installation is done in the file
230// thread and the network requests are done in the IO thread and in the file
231// thread.
232class CrxUpdateService : public ComponentUpdateService {
233 public:
234 explicit CrxUpdateService(ComponentUpdateService::Configurator* config);
235
236 virtual ~CrxUpdateService();
237
238 // Overrides for ComponentUpdateService.
239 virtual Status Start() OVERRIDE;
240 virtual Status Stop() OVERRIDE;
241 virtual Status RegisterComponent(const CrxComponent& component) OVERRIDE;
242
243 // The only purpose of this class is to forward the
[email protected]c4f883a2012-02-03 17:02:07244 // UtilityProcessHostClient callbacks so CrxUpdateService does
[email protected]e8f96ff2011-08-03 05:07:33245 // not have to derive from it because that is refcounted.
[email protected]c4f883a2012-02-03 17:02:07246 class ManifestParserBridge : public UtilityProcessHostClient {
[email protected]e8f96ff2011-08-03 05:07:33247 public:
248 explicit ManifestParserBridge(CrxUpdateService* service)
249 : service_(service) {}
250
251 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
252 bool handled = true;
253 IPC_BEGIN_MESSAGE_MAP(ManifestParserBridge, message)
[email protected]2ccf45c2011-08-19 23:35:50254 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseUpdateManifest_Succeeded,
[email protected]e8f96ff2011-08-03 05:07:33255 OnParseUpdateManifestSucceeded)
[email protected]2ccf45c2011-08-19 23:35:50256 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseUpdateManifest_Failed,
[email protected]e8f96ff2011-08-03 05:07:33257 OnParseUpdateManifestFailed)
258 IPC_MESSAGE_UNHANDLED(handled = false)
259 IPC_END_MESSAGE_MAP()
260 return handled;
261 }
262
263 private:
[email protected]a3988cc2012-04-27 05:07:18264 virtual ~ManifestParserBridge() {}
265
[email protected]f1050432012-02-15 01:35:46266 // Omaha update response XML was successfully parsed.
[email protected]e8f96ff2011-08-03 05:07:33267 void OnParseUpdateManifestSucceeded(const UpdateManifest::Results& r) {
268 service_->OnParseUpdateManifestSucceeded(r);
269 }
270 // Omaha update response XML could not be parsed.
271 void OnParseUpdateManifestFailed(const std::string& e) {
272 service_->OnParseUpdateManifestFailed(e);
273 }
274
275 CrxUpdateService* service_;
276 DISALLOW_COPY_AND_ASSIGN(ManifestParserBridge);
277 };
278
279 // Context for a update check url request. See DelegateWithContext above.
280 struct UpdateContext {
281 base::Time start;
282 UpdateContext() : start(base::Time::Now()) {}
283 };
284
285 // Context for a crx download url request. See DelegateWithContext above.
286 struct CRXContext {
287 ComponentInstaller* installer;
288 std::vector<uint8> pk_hash;
289 std::string id;
290 CRXContext() : installer(NULL) {}
291 };
292
[email protected]10c2d692012-05-11 05:32:23293 void OnURLFetchComplete(const net::URLFetcher* source,
[email protected]7cc6e5632011-10-25 17:56:12294 UpdateContext* context);
[email protected]e8f96ff2011-08-03 05:07:33295
[email protected]10c2d692012-05-11 05:32:23296 void OnURLFetchComplete(const net::URLFetcher* source,
[email protected]7cc6e5632011-10-25 17:56:12297 CRXContext* context);
[email protected]e8f96ff2011-08-03 05:07:33298
299 private:
300 // See ManifestParserBridge.
301 void OnParseUpdateManifestSucceeded(
302 const UpdateManifest::Results& results);
303
304 // See ManifestParserBridge.
305 void OnParseUpdateManifestFailed(
306 const std::string& error_message);
307
308 bool AddItemToUpdateCheck(CrxUpdateItem* item, std::string* query);
309
310 void ProcessPendingItems();
311
312 void ScheduleNextRun(bool step_delay);
313
314 void ParseManifest(const std::string& xml);
315
316 void Install(const CRXContext* context, const FilePath& crx_path);
317
[email protected]07f93af12011-08-17 20:57:22318 void DoneInstalling(const std::string& component_id,
319 ComponentUnpacker::Error error);
[email protected]e8f96ff2011-08-03 05:07:33320
321 size_t ChangeItemStatus(CrxUpdateItem::Status from,
322 CrxUpdateItem::Status to);
323
324 CrxUpdateItem* FindUpdateItemById(const std::string& id);
325
326 scoped_ptr<Config> config_;
327
[email protected]7cc6e5632011-10-25 17:56:12328 scoped_ptr<content::URLFetcher> url_fetcher_;
[email protected]e8f96ff2011-08-03 05:07:33329
330 typedef std::vector<CrxUpdateItem*> UpdateItems;
331 UpdateItems work_items_;
332
333 base::OneShotTimer<CrxUpdateService> timer_;
334
335 Version chrome_version_;
336
337 bool running_;
338
339 DISALLOW_COPY_AND_ASSIGN(CrxUpdateService);
340};
341
[email protected]e8f96ff2011-08-03 05:07:33342//////////////////////////////////////////////////////////////////////////////
343
344CrxUpdateService::CrxUpdateService(
345 ComponentUpdateService::Configurator* config)
346 : config_(config),
347 chrome_version_(chrome::VersionInfo().Version()),
348 running_(false) {
349}
350
351CrxUpdateService::~CrxUpdateService() {
352 // Because we are a singleton, at this point only the UI thread should be
353 // alive, this simplifies the management of the work that could be in
354 // flight in other threads.
355 Stop();
356 STLDeleteElements(&work_items_);
357}
358
359ComponentUpdateService::Status CrxUpdateService::Start() {
360 // Note that RegisterComponent will call Start() when the first
361 // component is registered, so it can be called twice. This way
362 // we avoid scheduling the timer if there is no work to do.
363 running_ = true;
364 if (work_items_.empty())
365 return kOk;
366
[email protected]ad50def52011-10-19 23:17:07367 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33368 chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED,
[email protected]6c2381d2011-10-19 02:52:53369 content::Source<ComponentUpdateService>(this),
[email protected]ad50def52011-10-19 23:17:07370 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33371
[email protected]d323a172011-09-02 18:23:02372 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(config_->InitialDelay()),
[email protected]e8f96ff2011-08-03 05:07:33373 this, &CrxUpdateService::ProcessPendingItems);
374 return kOk;
375}
376
377// Stop the main check + update loop. In flight operations will be
378// completed.
379ComponentUpdateService::Status CrxUpdateService::Stop() {
380 running_ = false;
381 timer_.Stop();
382 return kOk;
383}
384
385// This function sets the timer which will call ProcessPendingItems() there
386// are two kind of waits, the short one (with step_delay = true) and the
387// long one.
388void CrxUpdateService::ScheduleNextRun(bool step_delay) {
389 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]cf442612011-08-09 20:20:12390 DCHECK(url_fetcher_.get() == NULL);
[email protected]e8f96ff2011-08-03 05:07:33391 CHECK(!timer_.IsRunning());
392 // It could be the case that Stop() had been called while a url request
393 // or unpacking was in flight, if so we arrive here but |running_| is
394 // false. In that case do not loop again.
395 if (!running_)
396 return;
397
[email protected]cf442612011-08-09 20:20:12398 int64 delay = step_delay ? config_->StepDelay() : config_->NextCheckDelay();
399
[email protected]e8f96ff2011-08-03 05:07:33400 if (!step_delay) {
[email protected]ad50def52011-10-19 23:17:07401 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33402 chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING,
[email protected]6c2381d2011-10-19 02:52:53403 content::Source<ComponentUpdateService>(this),
[email protected]ad50def52011-10-19 23:17:07404 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33405 // Zero is only used for unit tests.
[email protected]cf442612011-08-09 20:20:12406 if (0 == delay)
[email protected]e8f96ff2011-08-03 05:07:33407 return;
408 }
409
[email protected]d323a172011-09-02 18:23:02410 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(delay),
[email protected]e8f96ff2011-08-03 05:07:33411 this, &CrxUpdateService::ProcessPendingItems);
412}
413
414// Given a extension-like component id, find the associated component.
415CrxUpdateItem* CrxUpdateService::FindUpdateItemById(const std::string& id) {
416 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
417 CrxUpdateItem::FindById finder(id);
418 UpdateItems::iterator it = std::find_if(work_items_.begin(),
419 work_items_.end(),
420 finder);
421 if (it == work_items_.end())
422 return NULL;
423 return (*it);
424}
425
426// Changes all the components in |work_items_| that have |from| status to
427// |to| statatus and returns how many have been changed.
428size_t CrxUpdateService::ChangeItemStatus(CrxUpdateItem::Status from,
429 CrxUpdateItem::Status to) {
430 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
431 size_t count = 0;
432 for (UpdateItems::iterator it = work_items_.begin();
433 it != work_items_.end(); ++it) {
434 CrxUpdateItem* item = *it;
435 if (item->status != from)
436 continue;
437 item->status = to;
438 ++count;
439 }
440 return count;
441}
442
443// Adds a component to be checked for upgrades. If the component exists it
444// it will be replaced and the return code is kReplaced.
445//
446// TODO(cpu): Evaluate if we want to support un-registration.
447ComponentUpdateService::Status CrxUpdateService::RegisterComponent(
448 const CrxComponent& component) {
449 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
450 if (component.pk_hash.empty() ||
451 !component.version.IsValid() ||
452 !component.installer)
453 return kError;
454
455 std::string id =
456 HexStringToID(StringToLowerASCII(base::HexEncode(&component.pk_hash[0],
457 component.pk_hash.size()/2)));
458 CrxUpdateItem* uit;
459 uit = FindUpdateItemById(id);
460 if (uit) {
461 uit->component = component;
462 return kReplaced;
463 }
464
465 uit = new CrxUpdateItem;
466 uit->id.swap(id);
467 uit->component = component;
468 work_items_.push_back(uit);
469 // If this is the first component registered we call Start to
470 // schedule the first timer.
471 if (running_ && (work_items_.size() == 1))
472 Start();
473
474 return kOk;
475}
476
477// Sets a component to be checked for updates.
478// The componet to add is |crxit| and the |query| string is modified with the
479// required omaha compatible query. Returns false when the query strings
480// is longer than specified by UrlSizeLimit().
481bool CrxUpdateService::AddItemToUpdateCheck(CrxUpdateItem* item,
482 std::string* query) {
483 if (!AddQueryString(item->id,
484 item->component.version.GetString(),
485 config_->UrlSizeLimit(), query))
486 return false;
487 item->status = CrxUpdateItem::kChecking;
488 item->last_check = base::Time::Now();
489 return true;
490}
491
492// Here is where the work gets scheduled. Given that our |work_items_| list
493// is expected to be ten or less items, we simply loop several times.
494void CrxUpdateService::ProcessPendingItems() {
495 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
496 // First check for ready upgrades and do one. The first
497 // step is to fetch the crx package.
498 for (UpdateItems::const_iterator it = work_items_.begin();
499 it != work_items_.end(); ++it) {
500 CrxUpdateItem* item = *it;
501 if (item->status != CrxUpdateItem::kCanUpdate)
502 continue;
503 // Found component to update, start the process.
504 item->status = CrxUpdateItem::kDownloading;
505 CRXContext* context = new CRXContext;
506 context->pk_hash = item->component.pk_hash;
507 context->id = item->id;
508 context->installer = item->component.installer;
[email protected]36aea2702011-10-26 01:12:22509 url_fetcher_.reset(content::URLFetcher::Create(
510 0, item->crx_url, content::URLFetcher::GET,
[email protected]e8f96ff2011-08-03 05:07:33511 MakeContextDelegate(this, context)));
512 StartFetch(url_fetcher_.get(), config_->RequestContext(), true);
513 return;
514 }
515
516 std::string query;
517 // If no pending upgrades, we check the if there are new
518 // components we have not checked against the server. We
519 // can batch a bunch in a single url request.
520 for (UpdateItems::const_iterator it = work_items_.begin();
521 it != work_items_.end(); ++it) {
522 CrxUpdateItem* item = *it;
523 if (item->status != CrxUpdateItem::kNew)
524 continue;
525 if (!AddItemToUpdateCheck(item, &query))
526 break;
527 }
528
529 // Next we can go back to components we already checked, here
530 // we can also batch them in a single url request, as long as
531 // we have not checked them recently.
[email protected]afa9e342012-04-06 02:51:32532 const base::TimeDelta min_delta_time =
[email protected]e8f96ff2011-08-03 05:07:33533 base::TimeDelta::FromSeconds(config_->MinimumReCheckWait());
534
535 for (UpdateItems::const_iterator it = work_items_.begin();
536 it != work_items_.end(); ++it) {
537 CrxUpdateItem* item = *it;
[email protected]cf442612011-08-09 20:20:12538 if ((item->status != CrxUpdateItem::kNoUpdate) &&
[email protected]e8f96ff2011-08-03 05:07:33539 (item->status != CrxUpdateItem::kUpToDate))
540 continue;
541 base::TimeDelta delta = base::Time::Now() - item->last_check;
542 if (delta < min_delta_time)
543 continue;
544 if (!AddItemToUpdateCheck(item, &query))
545 break;
546 }
547 // Finally, we check components that we already updated.
548 for (UpdateItems::const_iterator it = work_items_.begin();
549 it != work_items_.end(); ++it) {
550 CrxUpdateItem* item = *it;
551 if (item->status != CrxUpdateItem::kUpdated)
552 continue;
553 base::TimeDelta delta = base::Time::Now() - item->last_check;
554 if (delta < min_delta_time)
555 continue;
556 if (!AddItemToUpdateCheck(item, &query))
557 break;
558 }
559
560 if (query.empty()) {
561 // Next check after the long sleep.
562 ScheduleNextRun(false);
563 return;
564 }
565
566 // We got components to check. Start the url request.
[email protected]926d36332011-10-05 01:06:25567 const std::string full_query = MakeFinalQuery(config_->UpdateUrl().spec(),
568 query,
569 config_->ExtraRequestParams());
[email protected]36aea2702011-10-26 01:12:22570 url_fetcher_.reset(content::URLFetcher::Create(
571 0, GURL(full_query), content::URLFetcher::GET,
[email protected]e8f96ff2011-08-03 05:07:33572 MakeContextDelegate(this, new UpdateContext())));
573 StartFetch(url_fetcher_.get(), config_->RequestContext(), false);
574}
575
576// Caled when we got a response from the update server. It consists of an xml
577// document following the omaha update scheme.
[email protected]10c2d692012-05-11 05:32:23578void CrxUpdateService::OnURLFetchComplete(const net::URLFetcher* source,
[email protected]07f93af12011-08-17 20:57:22579 UpdateContext* context) {
[email protected]e8f96ff2011-08-03 05:07:33580 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
581 if (FetchSuccess(*source)) {
582 std::string xml;
583 source->GetResponseAsString(&xml);
[email protected]cf442612011-08-09 20:20:12584 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33585 ParseManifest(xml);
586 } else {
[email protected]cf442612011-08-09 20:20:12587 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33588 CrxUpdateService::OnParseUpdateManifestFailed("network error");
589 }
[email protected]07f93af12011-08-17 20:57:22590 delete context;
[email protected]e8f96ff2011-08-03 05:07:33591}
592
593// Parsing the manifest is either done right now for tests or in a sandboxed
594// process for the production environment. This mitigates the case where an
595// attacker was able to feed us a malicious xml string.
596void CrxUpdateService::ParseManifest(const std::string& xml) {
597 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
598 if (config_->InProcess()) {
599 UpdateManifest manifest;
600 if (!manifest.Parse(xml)) {
601 CrxUpdateService::OnParseUpdateManifestFailed(manifest.errors());
602 } else {
603 CrxUpdateService::OnParseUpdateManifestSucceeded(manifest.results());
604 }
605 } else {
[email protected]c4f883a2012-02-03 17:02:07606 UtilityProcessHost* host = UtilityProcessHost::Create(
607 new ManifestParserBridge(this), BrowserThread::UI);
608 host->EnableZygote();
[email protected]2ccf45c2011-08-19 23:35:50609 host->Send(new ChromeUtilityMsg_ParseUpdateManifest(xml));
[email protected]e8f96ff2011-08-03 05:07:33610 }
611}
612
613// A valid Omaha update check has arrived, from only the list of components that
614// we are currently upgrading we check for a match in which the server side
615// version is newer, if so we queue them for an upgrade. The next time we call
616// ProcessPendingItems() one of them will be drafted for the upgrade process.
617void CrxUpdateService::OnParseUpdateManifestSucceeded(
618 const UpdateManifest::Results& results) {
619 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
620 int update_pending = 0;
621 std::vector<UpdateManifest::Result>::const_iterator it;
622 for (it = results.list.begin(); it != results.list.end(); ++it) {
623 CrxUpdateItem* crx = FindUpdateItemById(it->extension_id);
624 if (!crx)
625 continue;
626
627 if (crx->status != CrxUpdateItem::kChecking)
628 continue; // Not updating this component now.
629
[email protected]360b8bb2011-09-01 21:48:06630 config_->OnEvent(Configurator::kManifestCheck, CrxIdtoUMAId(crx->id));
631
[email protected]e8f96ff2011-08-03 05:07:33632 if (it->version.empty()) {
[email protected]cf442612011-08-09 20:20:12633 // No version means no update available.
[email protected]e8f96ff2011-08-03 05:07:33634 crx->status = CrxUpdateItem::kNoUpdate;
[email protected]cf442612011-08-09 20:20:12635 continue;
[email protected]e8f96ff2011-08-03 05:07:33636 }
637 if (!IsVersionNewer(crx->component.version, it->version)) {
[email protected]cf442612011-08-09 20:20:12638 // Our component is up to date.
[email protected]e8f96ff2011-08-03 05:07:33639 crx->status = CrxUpdateItem::kUpToDate;
[email protected]cf442612011-08-09 20:20:12640 continue;
[email protected]e8f96ff2011-08-03 05:07:33641 }
642 if (!it->browser_min_version.empty()) {
[email protected]cf442612011-08-09 20:20:12643 if (IsVersionNewer(chrome_version_, it->browser_min_version)) {
644 // Does not apply for this chrome version.
645 crx->status = CrxUpdateItem::kNoUpdate;
646 continue;
647 }
[email protected]e8f96ff2011-08-03 05:07:33648 }
649 // All test passed. Queue an upgrade for this component and fire the
650 // notifications.
651 crx->crx_url = it->crx_url;
652 crx->status = CrxUpdateItem::kCanUpdate;
[email protected]07f93af12011-08-17 20:57:22653 crx->next_version = Version(it->version);
[email protected]e8f96ff2011-08-03 05:07:33654 ++update_pending;
655
[email protected]ad50def52011-10-19 23:17:07656 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33657 chrome::NOTIFICATION_COMPONENT_UPDATE_FOUND,
[email protected]6c2381d2011-10-19 02:52:53658 content::Source<std::string>(&crx->id),
[email protected]ad50def52011-10-19 23:17:07659 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33660 }
[email protected]cf442612011-08-09 20:20:12661
662 // All the components that are not mentioned in the manifest we
663 // consider them up to date.
664 ChangeItemStatus(CrxUpdateItem::kChecking, CrxUpdateItem::kUpToDate);
665
[email protected]e8f96ff2011-08-03 05:07:33666 // If there are updates pending we do a short wait.
667 ScheduleNextRun(update_pending ? true : false);
668}
669
670void CrxUpdateService::OnParseUpdateManifestFailed(
671 const std::string& error_message) {
672 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
673 size_t count = ChangeItemStatus(CrxUpdateItem::kChecking,
674 CrxUpdateItem::kNoUpdate);
[email protected]360b8bb2011-09-01 21:48:06675 config_->OnEvent(Configurator::kManifestError, static_cast<int>(count));
[email protected]e8f96ff2011-08-03 05:07:33676 DCHECK_GT(count, 0ul);
677 ScheduleNextRun(false);
678}
679
680// Called when the CRX package has been downloaded to a temporary location.
681// Here we fire the notifications and schedule the component-specific installer
682// to be called in the file thread.
[email protected]10c2d692012-05-11 05:32:23683void CrxUpdateService::OnURLFetchComplete(const net::URLFetcher* source,
[email protected]e8f96ff2011-08-03 05:07:33684 CRXContext* context) {
685 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
686 base::PlatformFileError error_code;
687
688 if (source->FileErrorOccurred(&error_code) || !FetchSuccess(*source)) {
689 size_t count = ChangeItemStatus(CrxUpdateItem::kDownloading,
690 CrxUpdateItem::kNoUpdate);
691 DCHECK_EQ(count, 1ul);
[email protected]360b8bb2011-09-01 21:48:06692 config_->OnEvent(Configurator::kNetworkError, CrxIdtoUMAId(context->id));
[email protected]cf442612011-08-09 20:20:12693 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33694 ScheduleNextRun(false);
695 } else {
696 FilePath temp_crx_path;
697 CHECK(source->GetResponseAsFilePath(true, &temp_crx_path));
698 size_t count = ChangeItemStatus(CrxUpdateItem::kDownloading,
699 CrxUpdateItem::kUpdating);
700 DCHECK_EQ(count, 1ul);
[email protected]cf442612011-08-09 20:20:12701 url_fetcher_.reset();
702
[email protected]ad50def52011-10-19 23:17:07703 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33704 chrome::NOTIFICATION_COMPONENT_UPDATE_READY,
[email protected]6c2381d2011-10-19 02:52:53705 content::Source<std::string>(&context->id),
[email protected]ad50def52011-10-19 23:17:07706 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33707
[email protected]44da56e2011-11-21 19:59:14708 // Why unretained? See comment at top of file.
[email protected]73251e72012-03-04 02:10:33709 BrowserThread::PostDelayedTask(
710 BrowserThread::FILE,
711 FROM_HERE,
[email protected]44da56e2011-11-21 19:59:14712 base::Bind(&CrxUpdateService::Install,
713 base::Unretained(this),
714 context,
715 temp_crx_path),
[email protected]73251e72012-03-04 02:10:33716 base::TimeDelta::FromMilliseconds(config_->StepDelay()));
[email protected]e8f96ff2011-08-03 05:07:33717 }
[email protected]e8f96ff2011-08-03 05:07:33718}
719
720// Install consists of digital signature verification, unpacking and then
721// calling the component specific installer. All that is handled by the
722// |unpacker|. If there is an error this function is in charge of deleting
723// the files created.
724void CrxUpdateService::Install(const CRXContext* context,
725 const FilePath& crx_path) {
726 // This function owns the |crx_path| and the |context| object.
727 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
728 ComponentUnpacker
729 unpacker(context->pk_hash, crx_path, context->installer);
[email protected]e8f96ff2011-08-03 05:07:33730 if (!file_util::Delete(crx_path, false)) {
731 NOTREACHED() << crx_path.value();
732 }
[email protected]44da56e2011-11-21 19:59:14733 // Why unretained? See comment at top of file.
[email protected]73251e72012-03-04 02:10:33734 BrowserThread::PostDelayedTask(
735 BrowserThread::UI,
736 FROM_HERE,
[email protected]44da56e2011-11-21 19:59:14737 base::Bind(&CrxUpdateService::DoneInstalling, base::Unretained(this),
738 context->id, unpacker.error()),
[email protected]73251e72012-03-04 02:10:33739 base::TimeDelta::FromMilliseconds(config_->StepDelay()));
[email protected]07f93af12011-08-17 20:57:22740 delete context;
[email protected]e8f96ff2011-08-03 05:07:33741}
742
743// Installation has been completed. Adjust the component status and
744// schedule the next check.
[email protected]07f93af12011-08-17 20:57:22745void CrxUpdateService::DoneInstalling(const std::string& component_id,
746 ComponentUnpacker::Error error) {
[email protected]e8f96ff2011-08-03 05:07:33747 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]07f93af12011-08-17 20:57:22748
749 CrxUpdateItem* item = FindUpdateItemById(component_id);
750 item->status = (error == ComponentUnpacker::kNone) ? CrxUpdateItem::kUpdated :
751 CrxUpdateItem::kNoUpdate;
752 if (item->status == CrxUpdateItem::kUpdated)
753 item->component.version = item->next_version;
754
[email protected]360b8bb2011-09-01 21:48:06755 Configurator::Events event;
756 switch (error) {
757 case ComponentUnpacker::kNone:
758 event = Configurator::kComponentUpdated;
759 break;
760 case ComponentUnpacker::kInstallerError:
761 event = Configurator::kInstallerError;
762 break;
763 default:
764 event = Configurator::kUnpackError;
765 break;
766 }
767
768 config_->OnEvent(event, CrxIdtoUMAId(component_id));
[email protected]e8f96ff2011-08-03 05:07:33769 ScheduleNextRun(false);
770}
771
772// The component update factory. Using the component updater as a singleton
773// is the job of the browser process.
774ComponentUpdateService* ComponentUpdateServiceFactory(
775 ComponentUpdateService::Configurator* config) {
776 DCHECK(config);
777 return new CrxUpdateService(config);
778}