| // Copyright (c) 2010 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/stateful_external_extension_provider.h" |
| |
| #include "app/app_paths.h" |
| #include "base/file_path.h" |
| #include "base/logging.h" |
| #include "base/path_service.h" |
| #include "base/values.h" |
| #include "base/version.h" |
| #include "chrome/browser/browser_thread.h" |
| |
| namespace { |
| |
| // Constants for keeping track of extension preferences. |
| const char kLocation[] = "location"; |
| const char kState[] = "state"; |
| const char kExternalCrx[] = "external_crx"; |
| const char kExternalVersion[] = "external_version"; |
| const char kExternalUpdateUrl[] = "external_update_url"; |
| |
| } |
| |
| StatefulExternalExtensionProvider::StatefulExternalExtensionProvider( |
| Extension::Location crx_location, |
| Extension::Location download_location) |
| : crx_location_(crx_location), |
| download_location_(download_location) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| } |
| |
| StatefulExternalExtensionProvider::~StatefulExternalExtensionProvider() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| } |
| |
| void StatefulExternalExtensionProvider::VisitRegisteredExtension( |
| Visitor* visitor) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| DCHECK(prefs_.get()); |
| for (DictionaryValue::key_iterator i = prefs_->begin_keys(); |
| i != prefs_->end_keys(); ++i) { |
| const std::string& extension_id = *i; |
| DictionaryValue* extension; |
| if (!prefs_->GetDictionaryWithoutPathExpansion(extension_id, &extension)) |
| 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, make it absolute. |
| FilePath path(external_crx); |
| if (!path.IsAbsolute()) { |
| // Try path as relative path from external extension dir. |
| FilePath base_path; |
| PathService::Get(app::DIR_EXTERNAL_EXTENSIONS, &base_path); |
| 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; |
| } |
| visitor->OnExternalExtensionFileFound(extension_id, version.get(), path, |
| crx_location_); |
| } else { // if (has_external_update_url) |
| DCHECK(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() << ". " << kExternalUpdateUrl |
| << " must be a valid URL. Saw \"" << external_update_url |
| << "\"."; |
| continue; |
| } |
| visitor->OnExternalExtensionUpdateUrlFound( |
| extension_id, update_url, download_location_); |
| } |
| } |
| } |
| |
| bool StatefulExternalExtensionProvider::HasExtension( |
| const std::string& id) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| DCHECK(prefs_.get()); |
| return prefs_->HasKey(id); |
| } |
| |
| bool StatefulExternalExtensionProvider::GetExtensionDetails( |
| const std::string& id, Extension::Location* location, |
| scoped_ptr<Version>* version) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| DCHECK(prefs_.get()); |
| 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; |
| } |
| |
| void StatefulExternalExtensionProvider::set_prefs(DictionaryValue* prefs) { |
| prefs_.reset(prefs); |
| } |