blob: 43bc3c9b284593ddd45381975b39ef41b9f37058 [file] [log] [blame]
[email protected]e8f96ff2011-08-03 05:07:331// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// 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"
11#include "base/file_path.h"
12#include "base/file_util.h"
13#include "base/logging.h"
[email protected]7226b33c2011-08-18 08:44:2214#include "base/memory/scoped_ptr.h"
[email protected]e8f96ff2011-08-03 05:07:3315#include "base/stl_util.h"
16#include "base/string_number_conversions.h"
17#include "base/string_util.h"
18#include "base/stringprintf.h"
19#include "base/timer.h"
20#include "chrome/browser/browser_process.h"
21#include "chrome/browser/component_updater/component_unpacker.h"
22#include "chrome/common/chrome_notification_types.h"
23#include "chrome/common/chrome_utility_messages.h"
24#include "chrome/common/chrome_version_info.h"
25#include "chrome/common/extensions/extension.h"
26#include "content/browser/utility_process_host.h"
[email protected]ffd7d732011-09-13 04:20:3427#include "content/common/net/url_fetcher.h"
[email protected]ad50def52011-10-19 23:17:0728#include "content/public/browser/notification_service.h"
[email protected]e8f96ff2011-08-03 05:07:3329#include "googleurl/src/gurl.h"
30#include "net/base/escape.h"
31#include "net/base/load_flags.h"
32
33namespace {
34// Extends an omaha compatible update check url |query| string. Does
35// not mutate the string if it would be longer than |limit| chars.
36bool AddQueryString(std::string id, std::string version,
37 size_t limit, std::string* query) {
38 std::string additional =
39 base::StringPrintf("id=%s&v=%s&uc", id.c_str(), version.c_str());
[email protected]4a19be92011-09-22 14:25:0240 additional = "x=" + net::EscapeQueryParamValue(additional, true);
[email protected]e8f96ff2011-08-03 05:07:3341 if ((additional.size() + query->size() + 1) > limit)
42 return false;
[email protected]926d36332011-10-05 01:06:2543 if (!query->empty())
44 query->append(1, '&');
[email protected]e8f96ff2011-08-03 05:07:3345 query->append(additional);
46 return true;
47}
48
[email protected]926d36332011-10-05 01:06:2549// Create the final omaha compatible query. The |extra| is optional and can
50// be null. It should contain top level (non-escaped) parameters.
51std::string MakeFinalQuery(const std::string& host,
52 const std::string& query,
53 const char* extra) {
54 std::string request(host);
55 request.append(1, '?');
56 if (extra) {
57 request.append(extra);
58 request.append(1, '&');
59 }
60 request.append(query);
61 return request;
62}
63
[email protected]e8f96ff2011-08-03 05:07:3364// Produces an extension-like friendly |id|. This might be removed in the
65// future if we roll our on packing tools.
66static std::string HexStringToID(const std::string& hexstr) {
67 std::string id;
68 for (size_t i = 0; i < hexstr.size(); ++i) {
69 int val;
70 if (base::HexStringToInt(hexstr.begin() + i, hexstr.begin() + i + 1, &val))
71 id.append(1, val + 'a');
72 else
73 id.append(1, 'a');
74 }
75 DCHECK(Extension::IdIsValid(id));
76 return id;
77}
78
[email protected]360b8bb2011-09-01 21:48:0679// Returns given a crx id it returns a small number, less than 100, that has a
80// decent chance of being unique among the registered components. It also has
81// the nice property that can be trivially computed by hand.
82static int CrxIdtoUMAId(const std::string& id) {
83 CHECK(id.size() > 2);
84 return id[0] + id[1] + id[2] - ('a' * 3);
85}
86
[email protected]e8f96ff2011-08-03 05:07:3387// Helper to do version check for components.
88bool IsVersionNewer(const Version& current, const std::string& proposed) {
89 Version proposed_ver(proposed);
90 if (!proposed_ver.IsValid())
91 return false;
92 return (current.CompareTo(proposed_ver) < 0);
93}
94
95// Helper template class that allows our main class to have separate
96// OnURLFetchComplete() callbacks for diffent types of url requests
97// they are differentiated by the |Ctx| type.
98template <typename Del, typename Ctx>
99class DelegateWithContext : public URLFetcher::Delegate {
100 public:
101 DelegateWithContext(Del* delegate, Ctx* context)
102 : delegate_(delegate), context_(context) {}
103
104 virtual void OnURLFetchComplete(const URLFetcher* source) OVERRIDE {
105 delegate_->OnURLFetchComplete(source, context_);
106 delete this;
107 }
108
109 private:
110 ~DelegateWithContext() {}
111
112 Del* delegate_;
113 Ctx* context_;
114};
115// This function creates the right DelegateWithContext using template inference.
116template <typename Del, typename Ctx>
117URLFetcher::Delegate* MakeContextDelegate(Del* delegate, Ctx* context) {
118 return new DelegateWithContext<Del, Ctx>(delegate, context);
119}
120
121// Helper to start a url request using |fetcher| with the common flags.
122void StartFetch(URLFetcher* fetcher,
123 net::URLRequestContextGetter* context_getter,
124 bool save_to_file) {
125 fetcher->set_request_context(context_getter);
126 fetcher->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES |
127 net::LOAD_DO_NOT_SAVE_COOKIES |
128 net::LOAD_DISABLE_CACHE);
129 // TODO(cpu): Define our retry and backoff policy.
130 fetcher->set_automatically_retry_on_5xx(false);
131 if (save_to_file) {
132 fetcher->SaveResponseToTemporaryFile(
133 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
134 }
135 fetcher->Start();
136}
137
138// Returs true if the url request of |fetcher| was succesful.
139bool FetchSuccess(const URLFetcher& fetcher) {
140 return (fetcher.status().status() == net::URLRequestStatus::SUCCESS) &&
141 (fetcher.response_code() == 200);
142}
143
144// This is the one and only per-item state structure. Designed to be hosted
145// in a std::vector or a std::list. The two main members are |component|
146// which is supplied by the the component updater client and |status| which
147// is modified as the item is processed by the update pipeline. The expected
148// transition graph is:
149// error error error
150// +--kNoUpdate<------<-------+------<------+------<------+
151// | | | |
152// V yes | | |
153// kNew --->kChecking-->[update?]----->kCanUpdate-->kDownloading-->kUpdating
154// ^ | |
155// | |no |
156// |--kUpToDate<---+ |
157// | success |
158// +--kUpdated<-------------------------------------------+
159//
160struct CrxUpdateItem {
161 enum Status {
162 kNew,
163 kChecking,
164 kCanUpdate,
165 kDownloading,
166 kUpdating,
167 kUpdated,
168 kUpToDate,
169 kNoUpdate,
170 kLastStatus
171 };
172
173 Status status;
174 GURL crx_url;
175 std::string id;
176 base::Time last_check;
177 CrxComponent component;
[email protected]07f93af12011-08-17 20:57:22178 Version next_version;
[email protected]e8f96ff2011-08-03 05:07:33179
180 CrxUpdateItem() : status(kNew) {}
181
182 // Function object used to find a specific component.
183 class FindById {
184 public:
185 explicit FindById(const std::string& id) : id_(id) {}
186
187 bool operator() (CrxUpdateItem* item) const {
188 return (item->id == id_);
189 }
190 private:
191 const std::string& id_;
192 };
193};
194
195} // namespace.
196
197typedef ComponentUpdateService::Configurator Config;
198
199CrxComponent::CrxComponent() {}
200CrxComponent::~CrxComponent() {}
201
202//////////////////////////////////////////////////////////////////////////////
203// The one and only implementation of the ComponentUpdateService interface. In
204// charge of running the show. The main method is ProcessPendingItems() which
205// is called periodically to do the updgrades/installs or the update checks.
206// An important consideration here is to be as "low impact" as we can to the
207// rest of the browser, so even if we have many components registered and
208// elegible for update, we only do one thing at a time with pauses in between
209// the tasks. Also when we do network requests there is only one |url_fetcher_|
210// in flight at at a time.
211// There are no locks in this code, the main structure |work_items_| is mutated
212// only from the UI thread. The unpack and installation is done in the file
213// thread and the network requests are done in the IO thread and in the file
214// thread.
215class CrxUpdateService : public ComponentUpdateService {
216 public:
217 explicit CrxUpdateService(ComponentUpdateService::Configurator* config);
218
219 virtual ~CrxUpdateService();
220
221 // Overrides for ComponentUpdateService.
222 virtual Status Start() OVERRIDE;
223 virtual Status Stop() OVERRIDE;
224 virtual Status RegisterComponent(const CrxComponent& component) OVERRIDE;
225
226 // The only purpose of this class is to forward the
227 // UtilityProcessHost::Client callbacks so CrxUpdateService does
228 // not have to derive from it because that is refcounted.
229 class ManifestParserBridge : public UtilityProcessHost::Client {
230 public:
231 explicit ManifestParserBridge(CrxUpdateService* service)
232 : service_(service) {}
233
234 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
235 bool handled = true;
236 IPC_BEGIN_MESSAGE_MAP(ManifestParserBridge, message)
[email protected]2ccf45c2011-08-19 23:35:50237 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseUpdateManifest_Succeeded,
[email protected]e8f96ff2011-08-03 05:07:33238 OnParseUpdateManifestSucceeded)
[email protected]2ccf45c2011-08-19 23:35:50239 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseUpdateManifest_Failed,
[email protected]e8f96ff2011-08-03 05:07:33240 OnParseUpdateManifestFailed)
241 IPC_MESSAGE_UNHANDLED(handled = false)
242 IPC_END_MESSAGE_MAP()
243 return handled;
244 }
245
246 private:
247 // Omaha update response XML was succesfuly parsed.
248 void OnParseUpdateManifestSucceeded(const UpdateManifest::Results& r) {
249 service_->OnParseUpdateManifestSucceeded(r);
250 }
251 // Omaha update response XML could not be parsed.
252 void OnParseUpdateManifestFailed(const std::string& e) {
253 service_->OnParseUpdateManifestFailed(e);
254 }
255
256 CrxUpdateService* service_;
257 DISALLOW_COPY_AND_ASSIGN(ManifestParserBridge);
258 };
259
260 // Context for a update check url request. See DelegateWithContext above.
261 struct UpdateContext {
262 base::Time start;
263 UpdateContext() : start(base::Time::Now()) {}
264 };
265
266 // Context for a crx download url request. See DelegateWithContext above.
267 struct CRXContext {
268 ComponentInstaller* installer;
269 std::vector<uint8> pk_hash;
270 std::string id;
271 CRXContext() : installer(NULL) {}
272 };
273
274 void OnURLFetchComplete(const URLFetcher* source, UpdateContext* context);
275
276 void OnURLFetchComplete(const URLFetcher* source, CRXContext* context);
277
278 private:
279 // See ManifestParserBridge.
280 void OnParseUpdateManifestSucceeded(
281 const UpdateManifest::Results& results);
282
283 // See ManifestParserBridge.
284 void OnParseUpdateManifestFailed(
285 const std::string& error_message);
286
287 bool AddItemToUpdateCheck(CrxUpdateItem* item, std::string* query);
288
289 void ProcessPendingItems();
290
291 void ScheduleNextRun(bool step_delay);
292
293 void ParseManifest(const std::string& xml);
294
295 void Install(const CRXContext* context, const FilePath& crx_path);
296
[email protected]07f93af12011-08-17 20:57:22297 void DoneInstalling(const std::string& component_id,
298 ComponentUnpacker::Error error);
[email protected]e8f96ff2011-08-03 05:07:33299
300 size_t ChangeItemStatus(CrxUpdateItem::Status from,
301 CrxUpdateItem::Status to);
302
303 CrxUpdateItem* FindUpdateItemById(const std::string& id);
304
305 scoped_ptr<Config> config_;
306
307 scoped_ptr<URLFetcher> url_fetcher_;
308
309 typedef std::vector<CrxUpdateItem*> UpdateItems;
310 UpdateItems work_items_;
311
312 base::OneShotTimer<CrxUpdateService> timer_;
313
314 Version chrome_version_;
315
316 bool running_;
317
318 DISALLOW_COPY_AND_ASSIGN(CrxUpdateService);
319};
320
321// The component updater is designed to live until process shutdown, besides
322// we can't be refcounted because we are a singleton.
323DISABLE_RUNNABLE_METHOD_REFCOUNT(CrxUpdateService);
324
325//////////////////////////////////////////////////////////////////////////////
326
327CrxUpdateService::CrxUpdateService(
328 ComponentUpdateService::Configurator* config)
329 : config_(config),
330 chrome_version_(chrome::VersionInfo().Version()),
331 running_(false) {
332}
333
334CrxUpdateService::~CrxUpdateService() {
335 // Because we are a singleton, at this point only the UI thread should be
336 // alive, this simplifies the management of the work that could be in
337 // flight in other threads.
338 Stop();
339 STLDeleteElements(&work_items_);
340}
341
342ComponentUpdateService::Status CrxUpdateService::Start() {
343 // Note that RegisterComponent will call Start() when the first
344 // component is registered, so it can be called twice. This way
345 // we avoid scheduling the timer if there is no work to do.
346 running_ = true;
347 if (work_items_.empty())
348 return kOk;
349
[email protected]ad50def52011-10-19 23:17:07350 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33351 chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED,
[email protected]6c2381d2011-10-19 02:52:53352 content::Source<ComponentUpdateService>(this),
[email protected]ad50def52011-10-19 23:17:07353 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33354
[email protected]d323a172011-09-02 18:23:02355 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(config_->InitialDelay()),
[email protected]e8f96ff2011-08-03 05:07:33356 this, &CrxUpdateService::ProcessPendingItems);
357 return kOk;
358}
359
360// Stop the main check + update loop. In flight operations will be
361// completed.
362ComponentUpdateService::Status CrxUpdateService::Stop() {
363 running_ = false;
364 timer_.Stop();
365 return kOk;
366}
367
368// This function sets the timer which will call ProcessPendingItems() there
369// are two kind of waits, the short one (with step_delay = true) and the
370// long one.
371void CrxUpdateService::ScheduleNextRun(bool step_delay) {
372 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]cf442612011-08-09 20:20:12373 DCHECK(url_fetcher_.get() == NULL);
[email protected]e8f96ff2011-08-03 05:07:33374 CHECK(!timer_.IsRunning());
375 // It could be the case that Stop() had been called while a url request
376 // or unpacking was in flight, if so we arrive here but |running_| is
377 // false. In that case do not loop again.
378 if (!running_)
379 return;
380
[email protected]cf442612011-08-09 20:20:12381 int64 delay = step_delay ? config_->StepDelay() : config_->NextCheckDelay();
382
[email protected]e8f96ff2011-08-03 05:07:33383 if (!step_delay) {
[email protected]ad50def52011-10-19 23:17:07384 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33385 chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING,
[email protected]6c2381d2011-10-19 02:52:53386 content::Source<ComponentUpdateService>(this),
[email protected]ad50def52011-10-19 23:17:07387 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33388 // Zero is only used for unit tests.
[email protected]cf442612011-08-09 20:20:12389 if (0 == delay)
[email protected]e8f96ff2011-08-03 05:07:33390 return;
391 }
392
[email protected]d323a172011-09-02 18:23:02393 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(delay),
[email protected]e8f96ff2011-08-03 05:07:33394 this, &CrxUpdateService::ProcessPendingItems);
395}
396
397// Given a extension-like component id, find the associated component.
398CrxUpdateItem* CrxUpdateService::FindUpdateItemById(const std::string& id) {
399 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
400 CrxUpdateItem::FindById finder(id);
401 UpdateItems::iterator it = std::find_if(work_items_.begin(),
402 work_items_.end(),
403 finder);
404 if (it == work_items_.end())
405 return NULL;
406 return (*it);
407}
408
409// Changes all the components in |work_items_| that have |from| status to
410// |to| statatus and returns how many have been changed.
411size_t CrxUpdateService::ChangeItemStatus(CrxUpdateItem::Status from,
412 CrxUpdateItem::Status to) {
413 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
414 size_t count = 0;
415 for (UpdateItems::iterator it = work_items_.begin();
416 it != work_items_.end(); ++it) {
417 CrxUpdateItem* item = *it;
418 if (item->status != from)
419 continue;
420 item->status = to;
421 ++count;
422 }
423 return count;
424}
425
426// Adds a component to be checked for upgrades. If the component exists it
427// it will be replaced and the return code is kReplaced.
428//
429// TODO(cpu): Evaluate if we want to support un-registration.
430ComponentUpdateService::Status CrxUpdateService::RegisterComponent(
431 const CrxComponent& component) {
432 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
433 if (component.pk_hash.empty() ||
434 !component.version.IsValid() ||
435 !component.installer)
436 return kError;
437
438 std::string id =
439 HexStringToID(StringToLowerASCII(base::HexEncode(&component.pk_hash[0],
440 component.pk_hash.size()/2)));
441 CrxUpdateItem* uit;
442 uit = FindUpdateItemById(id);
443 if (uit) {
444 uit->component = component;
445 return kReplaced;
446 }
447
448 uit = new CrxUpdateItem;
449 uit->id.swap(id);
450 uit->component = component;
451 work_items_.push_back(uit);
452 // If this is the first component registered we call Start to
453 // schedule the first timer.
454 if (running_ && (work_items_.size() == 1))
455 Start();
456
457 return kOk;
458}
459
460// Sets a component to be checked for updates.
461// The componet to add is |crxit| and the |query| string is modified with the
462// required omaha compatible query. Returns false when the query strings
463// is longer than specified by UrlSizeLimit().
464bool CrxUpdateService::AddItemToUpdateCheck(CrxUpdateItem* item,
465 std::string* query) {
466 if (!AddQueryString(item->id,
467 item->component.version.GetString(),
468 config_->UrlSizeLimit(), query))
469 return false;
470 item->status = CrxUpdateItem::kChecking;
471 item->last_check = base::Time::Now();
472 return true;
473}
474
475// Here is where the work gets scheduled. Given that our |work_items_| list
476// is expected to be ten or less items, we simply loop several times.
477void CrxUpdateService::ProcessPendingItems() {
478 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
479 // First check for ready upgrades and do one. The first
480 // step is to fetch the crx package.
481 for (UpdateItems::const_iterator it = work_items_.begin();
482 it != work_items_.end(); ++it) {
483 CrxUpdateItem* item = *it;
484 if (item->status != CrxUpdateItem::kCanUpdate)
485 continue;
486 // Found component to update, start the process.
487 item->status = CrxUpdateItem::kDownloading;
488 CRXContext* context = new CRXContext;
489 context->pk_hash = item->component.pk_hash;
490 context->id = item->id;
491 context->installer = item->component.installer;
492 url_fetcher_.reset(URLFetcher::Create(0, item->crx_url, URLFetcher::GET,
493 MakeContextDelegate(this, context)));
494 StartFetch(url_fetcher_.get(), config_->RequestContext(), true);
495 return;
496 }
497
498 std::string query;
499 // If no pending upgrades, we check the if there are new
500 // components we have not checked against the server. We
501 // can batch a bunch in a single url request.
502 for (UpdateItems::const_iterator it = work_items_.begin();
503 it != work_items_.end(); ++it) {
504 CrxUpdateItem* item = *it;
505 if (item->status != CrxUpdateItem::kNew)
506 continue;
507 if (!AddItemToUpdateCheck(item, &query))
508 break;
509 }
510
511 // Next we can go back to components we already checked, here
512 // we can also batch them in a single url request, as long as
513 // we have not checked them recently.
514 base::TimeDelta min_delta_time =
515 base::TimeDelta::FromSeconds(config_->MinimumReCheckWait());
516
517 for (UpdateItems::const_iterator it = work_items_.begin();
518 it != work_items_.end(); ++it) {
519 CrxUpdateItem* item = *it;
[email protected]cf442612011-08-09 20:20:12520 if ((item->status != CrxUpdateItem::kNoUpdate) &&
[email protected]e8f96ff2011-08-03 05:07:33521 (item->status != CrxUpdateItem::kUpToDate))
522 continue;
523 base::TimeDelta delta = base::Time::Now() - item->last_check;
524 if (delta < min_delta_time)
525 continue;
526 if (!AddItemToUpdateCheck(item, &query))
527 break;
528 }
529 // Finally, we check components that we already updated.
530 for (UpdateItems::const_iterator it = work_items_.begin();
531 it != work_items_.end(); ++it) {
532 CrxUpdateItem* item = *it;
533 if (item->status != CrxUpdateItem::kUpdated)
534 continue;
535 base::TimeDelta delta = base::Time::Now() - item->last_check;
536 if (delta < min_delta_time)
537 continue;
538 if (!AddItemToUpdateCheck(item, &query))
539 break;
540 }
541
542 if (query.empty()) {
543 // Next check after the long sleep.
544 ScheduleNextRun(false);
545 return;
546 }
547
548 // We got components to check. Start the url request.
[email protected]926d36332011-10-05 01:06:25549 const std::string full_query = MakeFinalQuery(config_->UpdateUrl().spec(),
550 query,
551 config_->ExtraRequestParams());
552 url_fetcher_.reset(URLFetcher::Create(0, GURL(full_query), URLFetcher::GET,
[email protected]e8f96ff2011-08-03 05:07:33553 MakeContextDelegate(this, new UpdateContext())));
554 StartFetch(url_fetcher_.get(), config_->RequestContext(), false);
555}
556
557// Caled when we got a response from the update server. It consists of an xml
558// document following the omaha update scheme.
559void CrxUpdateService::OnURLFetchComplete(const URLFetcher* source,
[email protected]07f93af12011-08-17 20:57:22560 UpdateContext* context) {
[email protected]e8f96ff2011-08-03 05:07:33561 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
562 if (FetchSuccess(*source)) {
563 std::string xml;
564 source->GetResponseAsString(&xml);
[email protected]cf442612011-08-09 20:20:12565 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33566 ParseManifest(xml);
567 } else {
[email protected]cf442612011-08-09 20:20:12568 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33569 CrxUpdateService::OnParseUpdateManifestFailed("network error");
570 }
[email protected]07f93af12011-08-17 20:57:22571 delete context;
[email protected]e8f96ff2011-08-03 05:07:33572}
573
574// Parsing the manifest is either done right now for tests or in a sandboxed
575// process for the production environment. This mitigates the case where an
576// attacker was able to feed us a malicious xml string.
577void CrxUpdateService::ParseManifest(const std::string& xml) {
578 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
579 if (config_->InProcess()) {
580 UpdateManifest manifest;
581 if (!manifest.Parse(xml)) {
582 CrxUpdateService::OnParseUpdateManifestFailed(manifest.errors());
583 } else {
584 CrxUpdateService::OnParseUpdateManifestSucceeded(manifest.results());
585 }
586 } else {
587 UtilityProcessHost* host =
588 new UtilityProcessHost(new ManifestParserBridge(this),
589 BrowserThread::UI);
[email protected]2ccf45c2011-08-19 23:35:50590 host->Send(new ChromeUtilityMsg_ParseUpdateManifest(xml));
[email protected]e8f96ff2011-08-03 05:07:33591 }
592}
593
594// A valid Omaha update check has arrived, from only the list of components that
595// we are currently upgrading we check for a match in which the server side
596// version is newer, if so we queue them for an upgrade. The next time we call
597// ProcessPendingItems() one of them will be drafted for the upgrade process.
598void CrxUpdateService::OnParseUpdateManifestSucceeded(
599 const UpdateManifest::Results& results) {
600 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
601 int update_pending = 0;
602 std::vector<UpdateManifest::Result>::const_iterator it;
603 for (it = results.list.begin(); it != results.list.end(); ++it) {
604 CrxUpdateItem* crx = FindUpdateItemById(it->extension_id);
605 if (!crx)
606 continue;
607
608 if (crx->status != CrxUpdateItem::kChecking)
609 continue; // Not updating this component now.
610
[email protected]360b8bb2011-09-01 21:48:06611 config_->OnEvent(Configurator::kManifestCheck, CrxIdtoUMAId(crx->id));
612
[email protected]e8f96ff2011-08-03 05:07:33613 if (it->version.empty()) {
[email protected]cf442612011-08-09 20:20:12614 // No version means no update available.
[email protected]e8f96ff2011-08-03 05:07:33615 crx->status = CrxUpdateItem::kNoUpdate;
[email protected]cf442612011-08-09 20:20:12616 continue;
[email protected]e8f96ff2011-08-03 05:07:33617 }
618 if (!IsVersionNewer(crx->component.version, it->version)) {
[email protected]cf442612011-08-09 20:20:12619 // Our component is up to date.
[email protected]e8f96ff2011-08-03 05:07:33620 crx->status = CrxUpdateItem::kUpToDate;
[email protected]cf442612011-08-09 20:20:12621 continue;
[email protected]e8f96ff2011-08-03 05:07:33622 }
623 if (!it->browser_min_version.empty()) {
[email protected]cf442612011-08-09 20:20:12624 if (IsVersionNewer(chrome_version_, it->browser_min_version)) {
625 // Does not apply for this chrome version.
626 crx->status = CrxUpdateItem::kNoUpdate;
627 continue;
628 }
[email protected]e8f96ff2011-08-03 05:07:33629 }
630 // All test passed. Queue an upgrade for this component and fire the
631 // notifications.
632 crx->crx_url = it->crx_url;
633 crx->status = CrxUpdateItem::kCanUpdate;
[email protected]07f93af12011-08-17 20:57:22634 crx->next_version = Version(it->version);
[email protected]e8f96ff2011-08-03 05:07:33635 ++update_pending;
636
[email protected]ad50def52011-10-19 23:17:07637 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33638 chrome::NOTIFICATION_COMPONENT_UPDATE_FOUND,
[email protected]6c2381d2011-10-19 02:52:53639 content::Source<std::string>(&crx->id),
[email protected]ad50def52011-10-19 23:17:07640 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33641 }
[email protected]cf442612011-08-09 20:20:12642
643 // All the components that are not mentioned in the manifest we
644 // consider them up to date.
645 ChangeItemStatus(CrxUpdateItem::kChecking, CrxUpdateItem::kUpToDate);
646
[email protected]e8f96ff2011-08-03 05:07:33647 // If there are updates pending we do a short wait.
648 ScheduleNextRun(update_pending ? true : false);
649}
650
651void CrxUpdateService::OnParseUpdateManifestFailed(
652 const std::string& error_message) {
653 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
654 size_t count = ChangeItemStatus(CrxUpdateItem::kChecking,
655 CrxUpdateItem::kNoUpdate);
[email protected]360b8bb2011-09-01 21:48:06656 config_->OnEvent(Configurator::kManifestError, static_cast<int>(count));
[email protected]e8f96ff2011-08-03 05:07:33657 DCHECK_GT(count, 0ul);
658 ScheduleNextRun(false);
659}
660
661// Called when the CRX package has been downloaded to a temporary location.
662// Here we fire the notifications and schedule the component-specific installer
663// to be called in the file thread.
664void CrxUpdateService::OnURLFetchComplete(const URLFetcher* source,
665 CRXContext* context) {
666 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
667 base::PlatformFileError error_code;
668
669 if (source->FileErrorOccurred(&error_code) || !FetchSuccess(*source)) {
670 size_t count = ChangeItemStatus(CrxUpdateItem::kDownloading,
671 CrxUpdateItem::kNoUpdate);
672 DCHECK_EQ(count, 1ul);
[email protected]360b8bb2011-09-01 21:48:06673 config_->OnEvent(Configurator::kNetworkError, CrxIdtoUMAId(context->id));
[email protected]cf442612011-08-09 20:20:12674 url_fetcher_.reset();
[email protected]e8f96ff2011-08-03 05:07:33675 ScheduleNextRun(false);
676 } else {
677 FilePath temp_crx_path;
678 CHECK(source->GetResponseAsFilePath(true, &temp_crx_path));
679 size_t count = ChangeItemStatus(CrxUpdateItem::kDownloading,
680 CrxUpdateItem::kUpdating);
681 DCHECK_EQ(count, 1ul);
[email protected]cf442612011-08-09 20:20:12682 url_fetcher_.reset();
683
[email protected]ad50def52011-10-19 23:17:07684 content::NotificationService::current()->Notify(
[email protected]e8f96ff2011-08-03 05:07:33685 chrome::NOTIFICATION_COMPONENT_UPDATE_READY,
[email protected]6c2381d2011-10-19 02:52:53686 content::Source<std::string>(&context->id),
[email protected]ad50def52011-10-19 23:17:07687 content::NotificationService::NoDetails());
[email protected]e8f96ff2011-08-03 05:07:33688
689 BrowserThread::PostDelayedTask(BrowserThread::FILE, FROM_HERE,
690 NewRunnableMethod(this, &CrxUpdateService::Install,
691 context,
692 temp_crx_path),
693 config_->StepDelay());
694 }
[email protected]e8f96ff2011-08-03 05:07:33695}
696
697// Install consists of digital signature verification, unpacking and then
698// calling the component specific installer. All that is handled by the
699// |unpacker|. If there is an error this function is in charge of deleting
700// the files created.
701void CrxUpdateService::Install(const CRXContext* context,
702 const FilePath& crx_path) {
703 // This function owns the |crx_path| and the |context| object.
704 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
705 ComponentUnpacker
706 unpacker(context->pk_hash, crx_path, context->installer);
[email protected]e8f96ff2011-08-03 05:07:33707 if (!file_util::Delete(crx_path, false)) {
708 NOTREACHED() << crx_path.value();
709 }
[email protected]e8f96ff2011-08-03 05:07:33710 BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE,
711 NewRunnableMethod(this, &CrxUpdateService::DoneInstalling,
[email protected]07f93af12011-08-17 20:57:22712 context->id, unpacker.error()),
[email protected]e8f96ff2011-08-03 05:07:33713 config_->StepDelay());
[email protected]07f93af12011-08-17 20:57:22714 delete context;
[email protected]e8f96ff2011-08-03 05:07:33715}
716
717// Installation has been completed. Adjust the component status and
718// schedule the next check.
[email protected]07f93af12011-08-17 20:57:22719void CrxUpdateService::DoneInstalling(const std::string& component_id,
720 ComponentUnpacker::Error error) {
[email protected]e8f96ff2011-08-03 05:07:33721 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]07f93af12011-08-17 20:57:22722
723 CrxUpdateItem* item = FindUpdateItemById(component_id);
724 item->status = (error == ComponentUnpacker::kNone) ? CrxUpdateItem::kUpdated :
725 CrxUpdateItem::kNoUpdate;
726 if (item->status == CrxUpdateItem::kUpdated)
727 item->component.version = item->next_version;
728
[email protected]360b8bb2011-09-01 21:48:06729 Configurator::Events event;
730 switch (error) {
731 case ComponentUnpacker::kNone:
732 event = Configurator::kComponentUpdated;
733 break;
734 case ComponentUnpacker::kInstallerError:
735 event = Configurator::kInstallerError;
736 break;
737 default:
738 event = Configurator::kUnpackError;
739 break;
740 }
741
742 config_->OnEvent(event, CrxIdtoUMAId(component_id));
[email protected]e8f96ff2011-08-03 05:07:33743 ScheduleNextRun(false);
744}
745
746// The component update factory. Using the component updater as a singleton
747// is the job of the browser process.
748ComponentUpdateService* ComponentUpdateServiceFactory(
749 ComponentUpdateService::Configurator* config) {
750 DCHECK(config);
751 return new CrxUpdateService(config);
752}