blob: f288af0e6ea4cc77d1d0f5f5bc01ff624855c046 [file] [log] [blame]
// Copyright (c) 2011 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/extensions/external_extension_provider_impl.h"
#include "base/file_path.h"
#include "base/logging.h"
#include "base/memory/linked_ptr.h"
#include "base/path_service.h"
#include "base/values.h"
#include "base/version.h"
#include "chrome/browser/extensions/external_extension_provider_interface.h"
#include "chrome/browser/extensions/external_policy_extension_loader.h"
#include "chrome/browser/extensions/external_pref_extension_loader.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_paths.h"
#include "content/browser/browser_thread.h"
#if defined(OS_WIN)
#include "chrome/browser/extensions/external_registry_extension_loader_win.h"
#endif
// Constants for keeping track of extension preferences in a dictionary.
const char ExternalExtensionProviderImpl::kLocation[] = "location";
const char ExternalExtensionProviderImpl::kState[] = "state";
const char ExternalExtensionProviderImpl::kExternalCrx[] = "external_crx";
const char ExternalExtensionProviderImpl::kExternalVersion[] =
"external_version";
const char ExternalExtensionProviderImpl::kExternalUpdateUrl[] =
"external_update_url";
ExternalExtensionProviderImpl::ExternalExtensionProviderImpl(
VisitorInterface* service,
ExternalExtensionLoader* loader,
Extension::Location crx_location,
Extension::Location download_location)
: crx_location_(crx_location),
download_location_(download_location),
service_(service),
prefs_(NULL),
ready_(false),
loader_(loader) {
loader_->Init(this);
}
ExternalExtensionProviderImpl::~ExternalExtensionProviderImpl() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
loader_->OwnerShutdown();
}
void ExternalExtensionProviderImpl::VisitRegisteredExtension() const {
// The loader will call back to SetPrefs.
loader_->StartLoading();
}
void ExternalExtensionProviderImpl::SetPrefs(DictionaryValue* prefs) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Check if the service is still alive. It is possible that it had went
// away while |loader_| was working on the FILE thread.
if (!service_) return;
prefs_.reset(prefs);
ready_ = true; // Queries for extensions are allowed from this point.
// Notify ExtensionService about all the extensions this provider has.
for (DictionaryValue::key_iterator i = prefs_->begin_keys();
i != prefs_->end_keys(); ++i) {
const std::string& extension_id = *i;
DictionaryValue* extension;
if (!Extension::IdIsValid(extension_id)) {
LOG(WARNING) << "Malformed extension dictionary: key "
<< extension_id.c_str() << " is not a valid id.";
continue;
}
if (!prefs_->GetDictionaryWithoutPathExpansion(extension_id, &extension)) {
LOG(WARNING) << "Malformed extension dictionary: key "
<< extension_id.c_str()
<< " has a value that is not a dictionary.";
continue;
}
FilePath::StringType external_crx;
std::string external_version;
std::string external_update_url;
bool has_external_crx = extension->GetString(kExternalCrx, &external_crx);
bool has_external_version = extension->GetString(kExternalVersion,
&external_version);
bool has_external_update_url = extension->GetString(kExternalUpdateUrl,
&external_update_url);
if (has_external_crx != has_external_version) {
LOG(WARNING) << "Malformed extension dictionary for extension: "
<< extension_id.c_str() << ". " << kExternalCrx
<< " and " << kExternalVersion << " must be used together.";
continue;
}
if (has_external_crx == has_external_update_url) {
LOG(WARNING) << "Malformed extension dictionary for extension: "
<< extension_id.c_str() << ". Exactly one of the "
<< "followng keys should be used: " << kExternalCrx
<< ", " << kExternalUpdateUrl << ".";
continue;
}
if (has_external_crx) {
if (crx_location_ == Extension::INVALID) {
LOG(WARNING) << "This provider does not support installing external "
<< "extensions from crx files.";
continue;
}
if (external_crx.find(FilePath::kParentDirectory) !=
base::StringPiece::npos) {
LOG(WARNING) << "Path traversal not allowed in path: "
<< external_crx.c_str();
continue;
}
// If the path is relative, and the provider has a base path,
// build the absolute path to the crx file.
FilePath path(external_crx);
if (!path.IsAbsolute()) {
FilePath base_path = loader_->GetBaseCrxFilePath();
if (base_path.empty()) {
LOG(WARNING) << "File path " << external_crx.c_str()
<< " is relative. An absolute path is required.";
continue;
}
path = base_path.Append(external_crx);
}
scoped_ptr<Version> version;
version.reset(Version::GetVersionFromString(external_version));
if (!version.get()) {
LOG(WARNING) << "Malformed extension dictionary for extension: "
<< extension_id.c_str() << ". Invalid version string \""
<< external_version << "\".";
continue;
}
service_->OnExternalExtensionFileFound(extension_id, version.get(), path,
crx_location_);
} else { // if (has_external_update_url)
CHECK(has_external_update_url); // Checking of keys above ensures this.
if (download_location_ == Extension::INVALID) {
LOG(WARNING) << "This provider does not support installing external "
<< "extensions from update URLs.";
continue;
}
GURL update_url(external_update_url);
if (!update_url.is_valid()) {
LOG(WARNING) << "Malformed extension dictionary for extension: "
<< extension_id.c_str() << ". Key " << kExternalUpdateUrl
<< " has value \"" << external_update_url
<< "\", which is not a valid URL.";
continue;
}
service_->OnExternalExtensionUpdateUrlFound(
extension_id, update_url, download_location_);
}
}
service_->OnExternalProviderReady();
}
void ExternalExtensionProviderImpl::ServiceShutdown() {
service_ = NULL;
}
bool ExternalExtensionProviderImpl::IsReady() {
return ready_;
}
bool ExternalExtensionProviderImpl::HasExtension(
const std::string& id) const {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
CHECK(prefs_.get());
CHECK(ready_);
return prefs_->HasKey(id);
}
bool ExternalExtensionProviderImpl::GetExtensionDetails(
const std::string& id, Extension::Location* location,
scoped_ptr<Version>* version) const {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
CHECK(prefs_.get());
CHECK(ready_);
DictionaryValue* extension = NULL;
if (!prefs_->GetDictionary(id, &extension))
return false;
Extension::Location loc = Extension::INVALID;
if (extension->HasKey(kExternalUpdateUrl)) {
loc = download_location_;
} else if (extension->HasKey(kExternalCrx)) {
loc = crx_location_;
std::string external_version;
if (!extension->GetString(kExternalVersion, &external_version))
return false;
if (version)
version->reset(Version::GetVersionFromString(external_version));
} else {
NOTREACHED(); // Chrome should not allow prefs to get into this state.
return false;
}
if (location)
*location = loc;
return true;
}
// static
void ExternalExtensionProviderImpl::CreateExternalProviders(
VisitorInterface* service,
Profile* profile,
ProviderCollection* provider_list) {
provider_list->push_back(
linked_ptr<ExternalExtensionProviderInterface>(
new ExternalExtensionProviderImpl(
service,
new ExternalPrefExtensionLoader(
chrome::DIR_EXTERNAL_EXTENSIONS),
Extension::EXTERNAL_PREF,
Extension::EXTERNAL_PREF_DOWNLOAD)));
#if defined(OS_CHROMEOS)
// Chrome OS specific source for OEM customization.
provider_list->push_back(
linked_ptr<ExternalExtensionProviderInterface>(
new ExternalExtensionProviderImpl(
service,
new ExternalPrefExtensionLoader(
chrome::DIR_USER_EXTERNAL_EXTENSIONS),
Extension::EXTERNAL_PREF,
Extension::EXTERNAL_PREF_DOWNLOAD)));
#endif
#if defined(OS_WIN)
provider_list->push_back(
linked_ptr<ExternalExtensionProviderInterface>(
new ExternalExtensionProviderImpl(
service,
new ExternalRegistryExtensionLoader,
Extension::EXTERNAL_REGISTRY,
Extension::INVALID)));
#endif
provider_list->push_back(
linked_ptr<ExternalExtensionProviderInterface>(
new ExternalExtensionProviderImpl(
service,
new ExternalPolicyExtensionLoader(profile),
Extension::INVALID,
Extension::EXTERNAL_POLICY_DOWNLOAD)));
}