Home | History | Annotate | Download | only in extensions
      1 // 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/extensions/external_extension_provider_impl.h"
      6 
      7 #include "app/app_paths.h"
      8 #include "base/file_path.h"
      9 #include "base/logging.h"
     10 #include "base/memory/linked_ptr.h"
     11 #include "base/path_service.h"
     12 #include "base/values.h"
     13 #include "base/version.h"
     14 #include "chrome/browser/extensions/external_extension_provider_interface.h"
     15 #include "chrome/browser/extensions/external_policy_extension_loader.h"
     16 #include "chrome/browser/extensions/external_pref_extension_loader.h"
     17 #include "chrome/browser/profiles/profile.h"
     18 #include "chrome/common/chrome_paths.h"
     19 #include "content/browser/browser_thread.h"
     20 
     21 #if defined(OS_WIN)
     22 #include "chrome/browser/extensions/external_registry_extension_loader_win.h"
     23 #endif
     24 
     25 // Constants for keeping track of extension preferences in a dictionary.
     26 const char ExternalExtensionProviderImpl::kLocation[] = "location";
     27 const char ExternalExtensionProviderImpl::kState[] = "state";
     28 const char ExternalExtensionProviderImpl::kExternalCrx[] = "external_crx";
     29 const char ExternalExtensionProviderImpl::kExternalVersion[] =
     30     "external_version";
     31 const char ExternalExtensionProviderImpl::kExternalUpdateUrl[] =
     32     "external_update_url";
     33 
     34 ExternalExtensionProviderImpl::ExternalExtensionProviderImpl(
     35     VisitorInterface* service,
     36     ExternalExtensionLoader* loader,
     37     Extension::Location crx_location,
     38     Extension::Location download_location)
     39   : crx_location_(crx_location),
     40     download_location_(download_location),
     41     service_(service),
     42     prefs_(NULL),
     43     ready_(false),
     44     loader_(loader) {
     45   loader_->Init(this);
     46 }
     47 
     48 ExternalExtensionProviderImpl::~ExternalExtensionProviderImpl() {
     49   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     50   loader_->OwnerShutdown();
     51 }
     52 
     53 void ExternalExtensionProviderImpl::VisitRegisteredExtension() const {
     54   // The loader will call back to SetPrefs.
     55   loader_->StartLoading();
     56 }
     57 
     58 void ExternalExtensionProviderImpl::SetPrefs(DictionaryValue* prefs) {
     59   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     60 
     61   // Check if the service is still alive. It is possible that it had went
     62   // away while |loader_| was working on the FILE thread.
     63   if (!service_) return;
     64 
     65   prefs_.reset(prefs);
     66   ready_ = true; // Queries for extensions are allowed from this point.
     67 
     68   // Notify ExtensionService about all the extensions this provider has.
     69   for (DictionaryValue::key_iterator i = prefs_->begin_keys();
     70        i != prefs_->end_keys(); ++i) {
     71     const std::string& extension_id = *i;
     72     DictionaryValue* extension;
     73 
     74     if (!Extension::IdIsValid(extension_id)) {
     75       LOG(WARNING) << "Malformed extension dictionary: key "
     76                    << extension_id.c_str() << " is not a valid id.";
     77       continue;
     78     }
     79 
     80     if (!prefs_->GetDictionaryWithoutPathExpansion(extension_id, &extension)) {
     81       LOG(WARNING) << "Malformed extension dictionary: key "
     82                    << extension_id.c_str()
     83                    << " has a value that is not a dictionary.";
     84       continue;
     85     }
     86 
     87     FilePath::StringType external_crx;
     88     std::string external_version;
     89     std::string external_update_url;
     90 
     91     bool has_external_crx = extension->GetString(kExternalCrx, &external_crx);
     92     bool has_external_version = extension->GetString(kExternalVersion,
     93                                                      &external_version);
     94     bool has_external_update_url = extension->GetString(kExternalUpdateUrl,
     95                                                         &external_update_url);
     96     if (has_external_crx != has_external_version) {
     97       LOG(WARNING) << "Malformed extension dictionary for extension: "
     98                    << extension_id.c_str() << ".  " << kExternalCrx
     99                    << " and " << kExternalVersion << " must be used together.";
    100       continue;
    101     }
    102 
    103     if (has_external_crx == has_external_update_url) {
    104       LOG(WARNING) << "Malformed extension dictionary for extension: "
    105                    << extension_id.c_str() << ".  Exactly one of the "
    106                    << "followng keys should be used: " << kExternalCrx
    107                    << ", " << kExternalUpdateUrl << ".";
    108       continue;
    109     }
    110 
    111     if (has_external_crx) {
    112       if (crx_location_ == Extension::INVALID) {
    113         LOG(WARNING) << "This provider does not support installing external "
    114                      << "extensions from crx files.";
    115         continue;
    116       }
    117       if (external_crx.find(FilePath::kParentDirectory) !=
    118           base::StringPiece::npos) {
    119         LOG(WARNING) << "Path traversal not allowed in path: "
    120                      << external_crx.c_str();
    121         continue;
    122       }
    123 
    124       // If the path is relative, and the provider has a base path,
    125       // build the absolute path to the crx file.
    126       FilePath path(external_crx);
    127       if (!path.IsAbsolute()) {
    128         FilePath base_path = loader_->GetBaseCrxFilePath();
    129         if (base_path.empty()) {
    130           LOG(WARNING) << "File path " << external_crx.c_str()
    131                        << " is relative.  An absolute path is required.";
    132           continue;
    133         }
    134         path = base_path.Append(external_crx);
    135       }
    136 
    137       scoped_ptr<Version> version;
    138       version.reset(Version::GetVersionFromString(external_version));
    139       if (!version.get()) {
    140         LOG(WARNING) << "Malformed extension dictionary for extension: "
    141                      << extension_id.c_str() << ".  Invalid version string \""
    142                      << external_version << "\".";
    143         continue;
    144       }
    145       service_->OnExternalExtensionFileFound(extension_id, version.get(), path,
    146                                              crx_location_);
    147     } else { // if (has_external_update_url)
    148       CHECK(has_external_update_url);  // Checking of keys above ensures this.
    149       if (download_location_ == Extension::INVALID) {
    150         LOG(WARNING) << "This provider does not support installing external "
    151                      << "extensions from update URLs.";
    152         continue;
    153       }
    154       GURL update_url(external_update_url);
    155       if (!update_url.is_valid()) {
    156         LOG(WARNING) << "Malformed extension dictionary for extension: "
    157                      << extension_id.c_str() << ".  Key " << kExternalUpdateUrl
    158                      << " has value \"" << external_update_url
    159                      << "\", which is not a valid URL.";
    160         continue;
    161       }
    162       service_->OnExternalExtensionUpdateUrlFound(
    163           extension_id, update_url, download_location_);
    164     }
    165   }
    166 
    167   service_->OnExternalProviderReady();
    168 }
    169 
    170 void ExternalExtensionProviderImpl::ServiceShutdown() {
    171   service_ = NULL;
    172 }
    173 
    174 bool ExternalExtensionProviderImpl::IsReady() {
    175   return ready_;
    176 }
    177 
    178 bool ExternalExtensionProviderImpl::HasExtension(
    179     const std::string& id) const {
    180   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    181   CHECK(prefs_.get());
    182   CHECK(ready_);
    183   return prefs_->HasKey(id);
    184 }
    185 
    186 bool ExternalExtensionProviderImpl::GetExtensionDetails(
    187     const std::string& id, Extension::Location* location,
    188     scoped_ptr<Version>* version) const {
    189   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    190   CHECK(prefs_.get());
    191   CHECK(ready_);
    192   DictionaryValue* extension = NULL;
    193   if (!prefs_->GetDictionary(id, &extension))
    194     return false;
    195 
    196   Extension::Location loc = Extension::INVALID;
    197   if (extension->HasKey(kExternalUpdateUrl)) {
    198     loc = download_location_;
    199 
    200   } else if (extension->HasKey(kExternalCrx)) {
    201     loc = crx_location_;
    202 
    203     std::string external_version;
    204     if (!extension->GetString(kExternalVersion, &external_version))
    205       return false;
    206 
    207     if (version)
    208       version->reset(Version::GetVersionFromString(external_version));
    209 
    210   } else {
    211     NOTREACHED();  // Chrome should not allow prefs to get into this state.
    212     return false;
    213   }
    214 
    215   if (location)
    216     *location = loc;
    217 
    218   return true;
    219 }
    220 
    221 // static
    222 void ExternalExtensionProviderImpl::CreateExternalProviders(
    223     VisitorInterface* service,
    224     Profile* profile,
    225     ProviderCollection* provider_list) {
    226   provider_list->push_back(
    227       linked_ptr<ExternalExtensionProviderInterface>(
    228           new ExternalExtensionProviderImpl(
    229               service,
    230               new ExternalPrefExtensionLoader(
    231                   app::DIR_EXTERNAL_EXTENSIONS),
    232               Extension::EXTERNAL_PREF,
    233               Extension::EXTERNAL_PREF_DOWNLOAD)));
    234 
    235 #if defined(OS_CHROMEOS)
    236   // Chrome OS specific source for OEM customization.
    237   provider_list->push_back(
    238       linked_ptr<ExternalExtensionProviderInterface>(
    239           new ExternalExtensionProviderImpl(
    240               service,
    241               new ExternalPrefExtensionLoader(
    242                   chrome::DIR_USER_EXTERNAL_EXTENSIONS),
    243               Extension::EXTERNAL_PREF,
    244               Extension::EXTERNAL_PREF_DOWNLOAD)));
    245 #endif
    246 #if defined(OS_WIN)
    247   provider_list->push_back(
    248       linked_ptr<ExternalExtensionProviderInterface>(
    249           new ExternalExtensionProviderImpl(
    250               service,
    251               new ExternalRegistryExtensionLoader,
    252               Extension::EXTERNAL_REGISTRY,
    253               Extension::INVALID)));
    254 #endif
    255   provider_list->push_back(
    256       linked_ptr<ExternalExtensionProviderInterface>(
    257           new ExternalExtensionProviderImpl(
    258               service,
    259               new ExternalPolicyExtensionLoader(profile),
    260               Extension::INVALID,
    261               Extension::EXTERNAL_POLICY_DOWNLOAD)));
    262 }
    263