Index: chrome/browser/policy/app_pack_updater.cc |
diff --git a/chrome/browser/policy/app_pack_updater.cc b/chrome/browser/policy/app_pack_updater.cc |
deleted file mode 100644 |
index 2a0b9de88214bf515726be2f0472b1c505eb46ac..0000000000000000000000000000000000000000 |
--- a/chrome/browser/policy/app_pack_updater.cc |
+++ /dev/null |
@@ -1,572 +0,0 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "chrome/browser/policy/app_pack_updater.h" |
- |
-#include "base/bind.h" |
-#include "base/bind_helpers.h" |
-#include "base/file_util.h" |
-#include "base/location.h" |
-#include "base/stl_util.h" |
-#include "base/string_util.h" |
-#include "base/values.h" |
-#include "base/version.h" |
-#include "chrome/browser/browser_process.h" |
-#include "chrome/browser/chromeos/settings/cros_settings.h" |
-#include "chrome/browser/chromeos/settings/cros_settings_names.h" |
-#include "chrome/browser/extensions/crx_installer.h" |
-#include "chrome/browser/extensions/external_loader.h" |
-#include "chrome/browser/extensions/external_provider_impl.h" |
-#include "chrome/browser/extensions/updater/extension_downloader.h" |
-#include "chrome/browser/policy/enterprise_install_attributes.h" |
-#include "chrome/common/chrome_notification_types.h" |
-#include "chrome/common/extensions/extension.h" |
-#include "chrome/common/extensions/extension_constants.h" |
-#include "content/public/browser/browser_thread.h" |
-#include "content/public/browser/notification_details.h" |
-#include "content/public/browser/notification_service.h" |
-#include "content/public/browser/notification_source.h" |
- |
-using content::BrowserThread; |
-using file_util::FileEnumerator; |
- |
-namespace policy { |
- |
-namespace { |
- |
-// Directory where the AppPack extensions are cached. |
-const char kAppPackCacheDir[] = "/var/cache/app_pack"; |
- |
-// File name extension for CRX files (not case sensitive). |
-const char kCRXFileExtension[] = ".crx"; |
- |
-} // namespace |
- |
-const char AppPackUpdater::kExtensionId[] = "extension-id"; |
-const char AppPackUpdater::kUpdateUrl[] = "update-url"; |
- |
-// A custom extensions::ExternalLoader that the AppPackUpdater creates and uses |
-// to publish AppPack updates to the extensions system. |
-class AppPackExternalLoader |
- : public extensions::ExternalLoader, |
- public base::SupportsWeakPtr<AppPackExternalLoader> { |
- public: |
- AppPackExternalLoader() {} |
- |
- // Used by the AppPackUpdater to update the current list of extensions. |
- // The format of |prefs| is detailed in the extensions::ExternalLoader/ |
- // Provider headers. |
- void SetCurrentAppPackExtensions(scoped_ptr<base::DictionaryValue> prefs) { |
- app_pack_prefs_.Swap(prefs.get()); |
- StartLoading(); |
- } |
- |
- // Implementation of extensions::ExternalLoader: |
- virtual void StartLoading() OVERRIDE { |
- prefs_.reset(app_pack_prefs_.DeepCopy()); |
- VLOG(1) << "AppPack extension loader publishing " |
- << app_pack_prefs_.size() << " crx files."; |
- LoadFinished(); |
- } |
- |
- protected: |
- virtual ~AppPackExternalLoader() {} |
- |
- private: |
- base::DictionaryValue app_pack_prefs_; |
- |
- DISALLOW_COPY_AND_ASSIGN(AppPackExternalLoader); |
-}; |
- |
-AppPackUpdater::AppPackUpdater(net::URLRequestContextGetter* request_context, |
- EnterpriseInstallAttributes* install_attributes) |
- : ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)), |
- initialized_(false), |
- created_extension_loader_(false), |
- request_context_(request_context), |
- install_attributes_(install_attributes) { |
- chromeos::CrosSettings::Get()->AddSettingsObserver(chromeos::kAppPack, this); |
- |
- if (install_attributes_->GetMode() == DEVICE_MODE_KIOSK) { |
- // Already in Kiosk mode, start loading. |
- BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
- base::Bind(&AppPackUpdater::Init, |
- weak_ptr_factory_.GetWeakPtr())); |
- } else { |
- // Linger until the device switches to DEVICE_MODE_KIOSK and the app pack |
- // device setting appears. |
- } |
-} |
- |
-AppPackUpdater::~AppPackUpdater() { |
- chromeos::CrosSettings::Get()->RemoveSettingsObserver( |
- chromeos::kAppPack, this); |
-} |
- |
-extensions::ExternalLoader* AppPackUpdater::CreateExternalLoader() { |
- if (created_extension_loader_) { |
- NOTREACHED(); |
- return NULL; |
- } |
- created_extension_loader_ = true; |
- AppPackExternalLoader* loader = new AppPackExternalLoader(); |
- extension_loader_ = loader->AsWeakPtr(); |
- |
- // The cache may have been already checked. In that case, load the current |
- // extensions into the loader immediately. |
- UpdateExtensionLoader(); |
- |
- return loader; |
-} |
- |
-void AppPackUpdater::SetScreenSaverUpdateCallback( |
- const AppPackUpdater::ScreenSaverUpdateCallback& callback) { |
- screen_saver_update_callback_ = callback; |
- if (!screen_saver_update_callback_.is_null() && !screen_saver_path_.empty()) { |
- BrowserThread::PostTask( |
- BrowserThread::UI, FROM_HERE, |
- base::Bind(screen_saver_update_callback_, screen_saver_path_)); |
- } |
-} |
- |
-void AppPackUpdater::Init() { |
- if (initialized_) |
- return; |
- |
- initialized_ = true; |
- worker_pool_token_ = BrowserThread::GetBlockingPool()->GetSequenceToken(); |
- notification_registrar_.Add( |
- this, |
- chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR, |
- content::NotificationService::AllBrowserContextsAndSources()); |
- LoadPolicy(); |
-} |
- |
-void AppPackUpdater::Observe(int type, |
- const content::NotificationSource& source, |
- const content::NotificationDetails& details) { |
- switch (type) { |
- case chrome::NOTIFICATION_SYSTEM_SETTING_CHANGED: |
- DCHECK_EQ(chromeos::kAppPack, |
- *content::Details<const std::string>(details).ptr()); |
- if (install_attributes_->GetMode() == DEVICE_MODE_KIOSK) { |
- if (!initialized_) |
- Init(); |
- else |
- LoadPolicy(); |
- } |
- break; |
- |
- case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR: { |
- extensions::CrxInstaller* installer = |
- content::Source<extensions::CrxInstaller>(source).ptr(); |
- OnDamagedFileDetected(installer->source_file()); |
- break; |
- } |
- |
- default: |
- NOTREACHED(); |
- } |
-} |
- |
-void AppPackUpdater::LoadPolicy() { |
- chromeos::CrosSettings* settings = chromeos::CrosSettings::Get(); |
- if (chromeos::CrosSettingsProvider::TRUSTED != settings->PrepareTrustedValues( |
- base::Bind(&AppPackUpdater::LoadPolicy, |
- weak_ptr_factory_.GetWeakPtr()))) { |
- return; |
- } |
- |
- app_pack_extensions_.clear(); |
- const base::Value* value = settings->GetPref(chromeos::kAppPack); |
- const base::ListValue* list = NULL; |
- if (value && value->GetAsList(&list)) { |
- for (base::ListValue::const_iterator it = list->begin(); |
- it != list->end(); ++it) { |
- base::DictionaryValue* dict = NULL; |
- if (!(*it)->GetAsDictionary(&dict)) { |
- LOG(WARNING) << "AppPack entry is not a dictionary, ignoring."; |
- continue; |
- } |
- std::string id; |
- std::string update_url; |
- if (dict->GetString(kExtensionId, &id) && |
- dict->GetString(kUpdateUrl, &update_url)) { |
- app_pack_extensions_[id] = update_url; |
- } else { |
- LOG(WARNING) << "Failed to read required fields for an AppPack entry, " |
- << "ignoring."; |
- } |
- } |
- } |
- |
- VLOG(1) << "Refreshed AppPack policy, got " << app_pack_extensions_.size() |
- << " entries."; |
- |
- value = settings->GetPref(chromeos::kScreenSaverExtensionId); |
- if (!value || !value->GetAsString(&screen_saver_id_)) { |
- screen_saver_id_.clear(); |
- SetScreenSaverPath(base::FilePath()); |
- } |
- |
- CheckCacheNow(); |
-} |
- |
-void AppPackUpdater::CheckCacheNow() { |
- std::set<std::string>* valid_ids = new std::set<std::string>(); |
- for (PolicyEntryMap::iterator it = app_pack_extensions_.begin(); |
- it != app_pack_extensions_.end(); ++it) { |
- valid_ids->insert(it->first); |
- } |
- PostBlockingTask(FROM_HERE, |
- base::Bind(&AppPackUpdater::BlockingCheckCache, |
- weak_ptr_factory_.GetWeakPtr(), |
- base::Owned(valid_ids))); |
-} |
- |
-// static |
-void AppPackUpdater::BlockingCheckCache( |
- base::WeakPtr<AppPackUpdater> app_pack_updater, |
- const std::set<std::string>* valid_ids) { |
- CacheEntryMap* entries = new CacheEntryMap(); |
- BlockingCheckCacheInternal(valid_ids, entries); |
- BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
- base::Bind(&AppPackUpdater::OnCacheUpdated, |
- app_pack_updater, |
- base::Owned(entries))); |
-} |
- |
-// static |
-void AppPackUpdater::BlockingCheckCacheInternal( |
- const std::set<std::string>* valid_ids, |
- CacheEntryMap* entries) { |
- // Start by verifying that the cache dir exists. |
- base::FilePath dir(kAppPackCacheDir); |
- if (!file_util::DirectoryExists(dir)) { |
- // Create it now. |
- if (!file_util::CreateDirectory(dir)) |
- LOG(ERROR) << "Failed to create AppPack directory at " << dir.value(); |
- // Nothing else to do. |
- return; |
- } |
- |
- // Enumerate all the files in the cache |dir|, including directories |
- // and symlinks. Each unrecognized file will be erased. |
- int types = FileEnumerator::FILES | FileEnumerator::DIRECTORIES | |
- FileEnumerator::SHOW_SYM_LINKS; |
- FileEnumerator enumerator(dir, false /* recursive */, types); |
- |
- for (base::FilePath path = enumerator.Next(); |
- !path.empty(); path = enumerator.Next()) { |
- FileEnumerator::FindInfo info; |
- enumerator.GetFindInfo(&info); |
- std::string basename = path.BaseName().value(); |
- |
- if (FileEnumerator::IsDirectory(info) || |
- file_util::IsLink(FileEnumerator::GetFilename(info))) { |
- LOG(ERROR) << "Erasing bad file in AppPack directory: " << basename; |
- file_util::Delete(path, true /* recursive */); |
- continue; |
- } |
- |
- // crx files in the cache are named <extension-id>-<version>.crx. |
- std::string id; |
- std::string version; |
- |
- if (EndsWith(basename, kCRXFileExtension, false /* case-sensitive */)) { |
- size_t n = basename.find('-'); |
- if (n != std::string::npos && n + 1 < basename.size() - 4) { |
- id = basename.substr(0, n); |
- // Size of |version| = total size - "<id>" - "-" - ".crx" |
- version = basename.substr(n + 1, basename.size() - 5 - id.size()); |
- } |
- } |
- |
- if (!extensions::Extension::IdIsValid(id)) { |
- LOG(ERROR) << "Bad AppPack extension id in cache: " << id; |
- id.clear(); |
- } else if (!ContainsKey(*valid_ids, id)) { |
- LOG(WARNING) << basename << " is in the cache but is not configured by " |
- << "the AppPack policy, and will be erased."; |
- id.clear(); |
- } |
- |
- if (!Version(version).IsValid()) { |
- LOG(ERROR) << "Bad AppPack extension version in cache: " << version; |
- version.clear(); |
- } |
- |
- if (id.empty() || version.empty()) { |
- LOG(ERROR) << "Invalid file in AppPack cache, erasing: " << basename; |
- file_util::Delete(path, true /* recursive */); |
- continue; |
- } |
- |
- // Enforce a lower-case id. |
- id = StringToLowerASCII(id); |
- |
- // File seems good so far. Make sure there isn't another entry with the |
- // same id but a different version. |
- |
- if (ContainsKey(*entries, id)) { |
- LOG(ERROR) << "Found two AppPack files for the same extension, will " |
- "erase the oldest version"; |
- CacheEntry& entry = (*entries)[id]; |
- Version vEntry(entry.cached_version); |
- Version vCurrent(version); |
- DCHECK(vEntry.IsValid()); |
- DCHECK(vCurrent.IsValid()); |
- if (vEntry.CompareTo(vCurrent) < 0) { |
- file_util::Delete(base::FilePath(entry.path), true /* recursive */); |
- entry.path = path.value(); |
- } else { |
- file_util::Delete(path, true /* recursive */); |
- } |
- continue; |
- } |
- |
- // This is the only file for this |id| so far, add it. |
- |
- CacheEntry& entry = (*entries)[id]; |
- entry.path = path.value(); |
- entry.cached_version = version; |
- } |
-} |
- |
-void AppPackUpdater::OnCacheUpdated(CacheEntryMap* cache_entries) { |
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- cached_extensions_.swap(*cache_entries); |
- |
- CacheEntryMap::iterator it = cached_extensions_.find(screen_saver_id_); |
- if (it != cached_extensions_.end()) |
- SetScreenSaverPath(base::FilePath(it->second.path)); |
- else |
- SetScreenSaverPath(base::FilePath()); |
- |
- VLOG(1) << "Updated AppPack cache, there are " << cached_extensions_.size() |
- << " extensions cached and " |
- << (screen_saver_path_.empty() ? "no" : "the") << " screensaver"; |
- UpdateExtensionLoader(); |
- DownloadMissingExtensions(); |
-} |
- |
-void AppPackUpdater::UpdateExtensionLoader() { |
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- if (!extension_loader_) { |
- VLOG(1) << "No AppPack loader created yet, not pushing extensions."; |
- return; |
- } |
- |
- // Build a DictionaryValue with the format that |
- // extensions::ExternalProviderImpl expects, containing info about the locally |
- // cached extensions. |
- |
- scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue()); |
- for (CacheEntryMap::iterator it = cached_extensions_.begin(); |
- it != cached_extensions_.end(); ++it) { |
- const std::string& id = it->first; |
- // The screensaver isn't installed into the Profile. |
- if (id == screen_saver_id_) |
- continue; |
- |
- base::DictionaryValue* dict = new base::DictionaryValue(); |
- dict->SetString(extensions::ExternalProviderImpl::kExternalCrx, |
- it->second.path); |
- dict->SetString(extensions::ExternalProviderImpl::kExternalVersion, |
- it->second.cached_version); |
- |
- // Include this optional flag if the extension's update url is the Webstore. |
- PolicyEntryMap::iterator policy_entry = app_pack_extensions_.find(id); |
- if (policy_entry != app_pack_extensions_.end() && |
- extension_urls::IsWebstoreUpdateUrl( |
- GURL(policy_entry->second))) { |
- dict->SetBoolean(extensions::ExternalProviderImpl::kIsFromWebstore, true); |
- } |
- |
- prefs->Set(it->first, dict); |
- |
- VLOG(1) << "Updating AppPack extension loader, added " << it->second.path; |
- } |
- |
- extension_loader_->SetCurrentAppPackExtensions(prefs.Pass()); |
-} |
- |
-void AppPackUpdater::DownloadMissingExtensions() { |
- // Check for updates for all extensions configured by the policy. Some of |
- // them may already be in the cache; only those with updated version will be |
- // downloaded, in that case. |
- if (!downloader_.get()) { |
- downloader_.reset(new extensions::ExtensionDownloader(this, |
- request_context_)); |
- } |
- for (PolicyEntryMap::iterator it = app_pack_extensions_.begin(); |
- it != app_pack_extensions_.end(); ++it) { |
- downloader_->AddPendingExtension(it->first, GURL(it->second), 0); |
- } |
- VLOG(1) << "Downloading AppPack update manifest now"; |
- downloader_->StartAllPending(); |
-} |
- |
-void AppPackUpdater::OnExtensionDownloadFailed( |
- const std::string& id, |
- extensions::ExtensionDownloaderDelegate::Error error, |
- const extensions::ExtensionDownloaderDelegate::PingResult& ping_result, |
- const std::set<int>& request_ids) { |
- if (error == NO_UPDATE_AVAILABLE) { |
- if (!ContainsKey(cached_extensions_, id)) |
- LOG(ERROR) << "AppPack extension " << id << " not found on update server"; |
- } else { |
- LOG(ERROR) << "AppPack failed to download extension " << id |
- << ", error " << error; |
- } |
-} |
- |
-void AppPackUpdater::OnExtensionDownloadFinished( |
- const std::string& id, |
- const base::FilePath& path, |
- const GURL& download_url, |
- const std::string& version, |
- const extensions::ExtensionDownloaderDelegate::PingResult& ping_result, |
- const std::set<int>& request_ids) { |
- // The explicit copy ctors are to make sure that Bind() binds a copy and not |
- // a reference to the arguments. |
- PostBlockingTask(FROM_HERE, |
- base::Bind(&AppPackUpdater::BlockingInstallCacheEntry, |
- weak_ptr_factory_.GetWeakPtr(), |
- std::string(id), |
- base::FilePath(path), |
- std::string(version))); |
-} |
- |
-void AppPackUpdater::OnBlacklistDownloadFinished( |
- const std::string& data, |
- const std::string& package_hash, |
- const std::string& version, |
- const extensions::ExtensionDownloaderDelegate::PingResult& ping_result, |
- const std::set<int>& request_ids) { |
- NOTREACHED(); |
-} |
- |
-bool AppPackUpdater::IsExtensionPending(const std::string& id) { |
- // Pending means that there is no installed version yet. |
- return ContainsKey(app_pack_extensions_, id) && |
- !ContainsKey(cached_extensions_, id); |
-} |
- |
-bool AppPackUpdater::GetExtensionExistingVersion(const std::string& id, |
- std::string* version) { |
- if (!ContainsKey(app_pack_extensions_, id) || |
- !ContainsKey(cached_extensions_, id)) { |
- return false; |
- } |
- |
- *version = cached_extensions_[id].cached_version; |
- return true; |
-} |
- |
-// static |
-void AppPackUpdater::BlockingInstallCacheEntry( |
- base::WeakPtr<AppPackUpdater> app_pack_updater, |
- const std::string& id, |
- const base::FilePath& path, |
- const std::string& version) { |
- Version version_validator(version); |
- if (!version_validator.IsValid()) { |
- LOG(ERROR) << "AppPack downloaded extension " << id << " but got bad " |
- << "version: " << version; |
- file_util::Delete(path, true /* recursive */); |
- return; |
- } |
- |
- std::string basename = id + "-" + version + kCRXFileExtension; |
- base::FilePath cache_dir(kAppPackCacheDir); |
- base::FilePath cached_crx_path = cache_dir.Append(basename); |
- |
- if (file_util::PathExists(cached_crx_path)) { |
- LOG(WARNING) << "AppPack downloaded a crx whose filename will overwrite " |
- << "an existing cached crx."; |
- file_util::Delete(cached_crx_path, true /* recursive */); |
- } |
- |
- if (!file_util::DirectoryExists(cache_dir)) { |
- LOG(ERROR) << "AppPack cache directory does not exist, creating now: " |
- << cache_dir.value(); |
- if (!file_util::CreateDirectory(cache_dir)) { |
- LOG(ERROR) << "Failed to create the AppPack cache dir!"; |
- file_util::Delete(path, true /* recursive */); |
- return; |
- } |
- } |
- |
- if (!file_util::Move(path, cached_crx_path)) { |
- LOG(ERROR) << "Failed to move AppPack crx from " << path.value() |
- << " to " << cached_crx_path.value(); |
- file_util::Delete(path, true /* recursive */); |
- return; |
- } |
- |
- BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
- base::Bind(&AppPackUpdater::OnCacheEntryInstalled, |
- app_pack_updater, |
- std::string(id), |
- cached_crx_path.value(), |
- std::string(version))); |
-} |
- |
-void AppPackUpdater::OnCacheEntryInstalled(const std::string& id, |
- const std::string& path, |
- const std::string& version) { |
- VLOG(1) << "AppPack installed a new extension in the cache: " << path; |
- // Add to the list of cached extensions. |
- CacheEntry& entry = cached_extensions_[id]; |
- entry.path = path; |
- entry.cached_version = version; |
- |
- if (id == screen_saver_id_) { |
- VLOG(1) << "AppPack got the screen saver extension at " << path; |
- SetScreenSaverPath(base::FilePath(path)); |
- } else { |
- UpdateExtensionLoader(); |
- } |
-} |
- |
-void AppPackUpdater::OnDamagedFileDetected(const base::FilePath& path) { |
- // Search for |path| in |cached_extensions_|, and delete it if found. |
- for (CacheEntryMap::iterator it = cached_extensions_.begin(); |
- it != cached_extensions_.end(); ++it) { |
- if (it->second.path == path.value()) { |
- LOG(ERROR) << "AppPack extension at " << path.value() << " failed to " |
- << "install, deleting it."; |
- cached_extensions_.erase(it); |
- UpdateExtensionLoader(); |
- |
- // The file will be downloaded again on the next restart. |
- BrowserThread::PostTask( |
- BrowserThread::FILE, FROM_HERE, |
- base::Bind(base::IgnoreResult(file_util::Delete), path, true)); |
- |
- // Don't try to DownloadMissingExtensions() from here, |
- // since it can cause a fail/retry loop. |
- break; |
- } |
- } |
-} |
- |
-void AppPackUpdater::PostBlockingTask(const tracked_objects::Location& location, |
- const base::Closure& task) { |
- BrowserThread::GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior( |
- worker_pool_token_, location, task, |
- base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); |
-} |
- |
-void AppPackUpdater::SetScreenSaverPath(const base::FilePath& path) { |
- // Don't invoke the callback if the path isn't changing. |
- if (path != screen_saver_path_) { |
- screen_saver_path_ = path; |
- if (!screen_saver_update_callback_.is_null()) |
- screen_saver_update_callback_.Run(screen_saver_path_); |
- } |
-} |
- |
-} // namespace policy |