[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 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 | |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 7 | #include "base/file_path.h" |
| 8 | #include "base/logging.h" |
[email protected] | 3b63f8f4 | 2011-03-28 01:54:15 | [diff] [blame] | 9 | #include "base/memory/linked_ptr.h" |
[email protected] | 51a7a9d | 2011-09-27 17:21:41 | [diff] [blame] | 10 | #include "base/metrics/field_trial.h" |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 11 | #include "base/path_service.h" |
[email protected] | e601e82 | 2011-10-05 19:25:37 | [diff] [blame^] | 12 | #include "base/string_util.h" |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 13 | #include "base/values.h" |
| 14 | #include "base/version.h" |
[email protected] | e601e82 | 2011-10-05 19:25:37 | [diff] [blame^] | 15 | #include "chrome/browser/browser_process.h" |
[email protected] | 51a7a9d | 2011-09-27 17:21:41 | [diff] [blame] | 16 | #include "chrome/browser/extensions/default_apps_trial.h" |
| 17 | #include "chrome/browser/extensions/extension_service.h" |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 18 | #include "chrome/browser/extensions/external_extension_provider_interface.h" |
| 19 | #include "chrome/browser/extensions/external_policy_extension_loader.h" |
| 20 | #include "chrome/browser/extensions/external_pref_extension_loader.h" |
| 21 | #include "chrome/browser/profiles/profile.h" |
[email protected] | f0841cd | 2011-01-19 15:07:24 | [diff] [blame] | 22 | #include "chrome/common/chrome_paths.h" |
[email protected] | 5f945a0e | 2011-03-01 17:47:53 | [diff] [blame] | 23 | #include "content/browser/browser_thread.h" |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 24 | |
| 25 | #if defined(OS_WIN) |
| 26 | #include "chrome/browser/extensions/external_registry_extension_loader_win.h" |
| 27 | #endif |
| 28 | |
| 29 | // Constants for keeping track of extension preferences in a dictionary. |
| 30 | const char ExternalExtensionProviderImpl::kLocation[] = "location"; |
| 31 | const char ExternalExtensionProviderImpl::kState[] = "state"; |
| 32 | const char ExternalExtensionProviderImpl::kExternalCrx[] = "external_crx"; |
| 33 | const char ExternalExtensionProviderImpl::kExternalVersion[] = |
| 34 | "external_version"; |
| 35 | const char ExternalExtensionProviderImpl::kExternalUpdateUrl[] = |
| 36 | "external_update_url"; |
| 37 | |
[email protected] | 98b4aca6 | 2011-09-28 01:27:23 | [diff] [blame] | 38 | #if !defined(OS_CHROMEOS) |
[email protected] | 51a7a9d | 2011-09-27 17:21:41 | [diff] [blame] | 39 | class DefaultAppsProvider : public ExternalExtensionProviderImpl { |
| 40 | public: |
| 41 | DefaultAppsProvider(VisitorInterface* service, Profile* profile) |
| 42 | : ExternalExtensionProviderImpl(service, |
| 43 | new ExternalPrefExtensionLoader(chrome::DIR_DEFAULT_APPS, |
| 44 | ExternalPrefExtensionLoader::NONE), |
| 45 | Extension::EXTERNAL_PREF, Extension::INVALID), |
| 46 | profile_(profile) { |
| 47 | DCHECK(profile_); |
| 48 | } |
| 49 | |
| 50 | // ExternalExtensionProviderImpl overrides: |
| 51 | virtual void ServiceShutdown() OVERRIDE; |
| 52 | virtual void VisitRegisteredExtension() const OVERRIDE; |
| 53 | |
| 54 | private: |
| 55 | Profile* profile_; |
| 56 | |
| 57 | DISALLOW_COPY_AND_ASSIGN(DefaultAppsProvider); |
| 58 | }; |
| 59 | |
| 60 | void DefaultAppsProvider::ServiceShutdown() { |
| 61 | profile_ = NULL; |
| 62 | ExternalExtensionProviderImpl::ServiceShutdown(); |
| 63 | } |
| 64 | |
| 65 | void DefaultAppsProvider::VisitRegisteredExtension() const { |
| 66 | // Don't install default apps if the profile already has apps installed. |
| 67 | if (profile_) { |
| 68 | ExtensionService* extension_service = profile_->GetExtensionService(); |
| 69 | if (extension_service && extension_service->HasApps()) { |
| 70 | service()->OnExternalProviderReady(); |
| 71 | return; |
| 72 | } |
| 73 | } |
| 74 | |
| 75 | ExternalExtensionProviderImpl::VisitRegisteredExtension(); |
| 76 | } |
[email protected] | 98b4aca6 | 2011-09-28 01:27:23 | [diff] [blame] | 77 | #endif |
[email protected] | 51a7a9d | 2011-09-27 17:21:41 | [diff] [blame] | 78 | |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 79 | ExternalExtensionProviderImpl::ExternalExtensionProviderImpl( |
| 80 | VisitorInterface* service, |
| 81 | ExternalExtensionLoader* loader, |
| 82 | Extension::Location crx_location, |
| 83 | Extension::Location download_location) |
| 84 | : crx_location_(crx_location), |
| 85 | download_location_(download_location), |
| 86 | service_(service), |
| 87 | prefs_(NULL), |
| 88 | ready_(false), |
| 89 | loader_(loader) { |
| 90 | loader_->Init(this); |
| 91 | } |
| 92 | |
| 93 | ExternalExtensionProviderImpl::~ExternalExtensionProviderImpl() { |
| 94 | CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 95 | loader_->OwnerShutdown(); |
| 96 | } |
| 97 | |
| 98 | void ExternalExtensionProviderImpl::VisitRegisteredExtension() const { |
| 99 | // The loader will call back to SetPrefs. |
| 100 | loader_->StartLoading(); |
| 101 | } |
| 102 | |
| 103 | void ExternalExtensionProviderImpl::SetPrefs(DictionaryValue* prefs) { |
| 104 | CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 105 | |
| 106 | // Check if the service is still alive. It is possible that it had went |
| 107 | // away while |loader_| was working on the FILE thread. |
| 108 | if (!service_) return; |
| 109 | |
| 110 | prefs_.reset(prefs); |
| 111 | ready_ = true; // Queries for extensions are allowed from this point. |
| 112 | |
| 113 | // Notify ExtensionService about all the extensions this provider has. |
| 114 | for (DictionaryValue::key_iterator i = prefs_->begin_keys(); |
| 115 | i != prefs_->end_keys(); ++i) { |
| 116 | const std::string& extension_id = *i; |
| 117 | DictionaryValue* extension; |
[email protected] | ab22ba4 | 2011-01-14 16:36:38 | [diff] [blame] | 118 | |
| 119 | if (!Extension::IdIsValid(extension_id)) { |
| 120 | LOG(WARNING) << "Malformed extension dictionary: key " |
| 121 | << extension_id.c_str() << " is not a valid id."; |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 122 | continue; |
[email protected] | ab22ba4 | 2011-01-14 16:36:38 | [diff] [blame] | 123 | } |
| 124 | |
| 125 | if (!prefs_->GetDictionaryWithoutPathExpansion(extension_id, &extension)) { |
| 126 | LOG(WARNING) << "Malformed extension dictionary: key " |
| 127 | << extension_id.c_str() |
| 128 | << " has a value that is not a dictionary."; |
| 129 | continue; |
| 130 | } |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 131 | |
| 132 | FilePath::StringType external_crx; |
| 133 | std::string external_version; |
| 134 | std::string external_update_url; |
| 135 | |
| 136 | bool has_external_crx = extension->GetString(kExternalCrx, &external_crx); |
| 137 | bool has_external_version = extension->GetString(kExternalVersion, |
| 138 | &external_version); |
| 139 | bool has_external_update_url = extension->GetString(kExternalUpdateUrl, |
| 140 | &external_update_url); |
| 141 | if (has_external_crx != has_external_version) { |
| 142 | LOG(WARNING) << "Malformed extension dictionary for extension: " |
| 143 | << extension_id.c_str() << ". " << kExternalCrx |
| 144 | << " and " << kExternalVersion << " must be used together."; |
| 145 | continue; |
| 146 | } |
| 147 | |
| 148 | if (has_external_crx == has_external_update_url) { |
| 149 | LOG(WARNING) << "Malformed extension dictionary for extension: " |
| 150 | << extension_id.c_str() << ". Exactly one of the " |
| 151 | << "followng keys should be used: " << kExternalCrx |
| 152 | << ", " << kExternalUpdateUrl << "."; |
| 153 | continue; |
| 154 | } |
| 155 | |
| 156 | if (has_external_crx) { |
| 157 | if (crx_location_ == Extension::INVALID) { |
| 158 | LOG(WARNING) << "This provider does not support installing external " |
| 159 | << "extensions from crx files."; |
| 160 | continue; |
| 161 | } |
| 162 | if (external_crx.find(FilePath::kParentDirectory) != |
| 163 | base::StringPiece::npos) { |
| 164 | LOG(WARNING) << "Path traversal not allowed in path: " |
| 165 | << external_crx.c_str(); |
| 166 | continue; |
| 167 | } |
| 168 | |
[email protected] | f0841cd | 2011-01-19 15:07:24 | [diff] [blame] | 169 | // If the path is relative, and the provider has a base path, |
| 170 | // build the absolute path to the crx file. |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 171 | FilePath path(external_crx); |
| 172 | if (!path.IsAbsolute()) { |
[email protected] | f0841cd | 2011-01-19 15:07:24 | [diff] [blame] | 173 | FilePath base_path = loader_->GetBaseCrxFilePath(); |
| 174 | if (base_path.empty()) { |
| 175 | LOG(WARNING) << "File path " << external_crx.c_str() |
| 176 | << " is relative. An absolute path is required."; |
| 177 | continue; |
| 178 | } |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 179 | path = base_path.Append(external_crx); |
| 180 | } |
| 181 | |
| 182 | scoped_ptr<Version> version; |
| 183 | version.reset(Version::GetVersionFromString(external_version)); |
| 184 | if (!version.get()) { |
| 185 | LOG(WARNING) << "Malformed extension dictionary for extension: " |
| 186 | << extension_id.c_str() << ". Invalid version string \"" |
| 187 | << external_version << "\"."; |
| 188 | continue; |
| 189 | } |
| 190 | service_->OnExternalExtensionFileFound(extension_id, version.get(), path, |
[email protected] | a29a517a | 2011-01-21 21:11:12 | [diff] [blame] | 191 | crx_location_); |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 192 | } else { // if (has_external_update_url) |
| 193 | CHECK(has_external_update_url); // Checking of keys above ensures this. |
| 194 | if (download_location_ == Extension::INVALID) { |
| 195 | LOG(WARNING) << "This provider does not support installing external " |
| 196 | << "extensions from update URLs."; |
| 197 | continue; |
| 198 | } |
| 199 | GURL update_url(external_update_url); |
| 200 | if (!update_url.is_valid()) { |
| 201 | LOG(WARNING) << "Malformed extension dictionary for extension: " |
[email protected] | ab22ba4 | 2011-01-14 16:36:38 | [diff] [blame] | 202 | << extension_id.c_str() << ". Key " << kExternalUpdateUrl |
| 203 | << " has value \"" << external_update_url |
| 204 | << "\", which is not a valid URL."; |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 205 | continue; |
| 206 | } |
| 207 | service_->OnExternalExtensionUpdateUrlFound( |
| 208 | extension_id, update_url, download_location_); |
| 209 | } |
| 210 | } |
| 211 | |
| 212 | service_->OnExternalProviderReady(); |
| 213 | } |
| 214 | |
| 215 | void ExternalExtensionProviderImpl::ServiceShutdown() { |
| 216 | service_ = NULL; |
| 217 | } |
| 218 | |
| 219 | bool ExternalExtensionProviderImpl::IsReady() { |
| 220 | return ready_; |
| 221 | } |
| 222 | |
| 223 | bool ExternalExtensionProviderImpl::HasExtension( |
| 224 | const std::string& id) const { |
| 225 | CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 226 | CHECK(prefs_.get()); |
| 227 | CHECK(ready_); |
| 228 | return prefs_->HasKey(id); |
| 229 | } |
| 230 | |
| 231 | bool ExternalExtensionProviderImpl::GetExtensionDetails( |
| 232 | const std::string& id, Extension::Location* location, |
| 233 | scoped_ptr<Version>* version) const { |
| 234 | CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 235 | CHECK(prefs_.get()); |
| 236 | CHECK(ready_); |
| 237 | DictionaryValue* extension = NULL; |
| 238 | if (!prefs_->GetDictionary(id, &extension)) |
| 239 | return false; |
| 240 | |
| 241 | Extension::Location loc = Extension::INVALID; |
| 242 | if (extension->HasKey(kExternalUpdateUrl)) { |
| 243 | loc = download_location_; |
| 244 | |
| 245 | } else if (extension->HasKey(kExternalCrx)) { |
| 246 | loc = crx_location_; |
| 247 | |
| 248 | std::string external_version; |
| 249 | if (!extension->GetString(kExternalVersion, &external_version)) |
| 250 | return false; |
| 251 | |
| 252 | if (version) |
| 253 | version->reset(Version::GetVersionFromString(external_version)); |
| 254 | |
| 255 | } else { |
| 256 | NOTREACHED(); // Chrome should not allow prefs to get into this state. |
| 257 | return false; |
| 258 | } |
| 259 | |
| 260 | if (location) |
| 261 | *location = loc; |
| 262 | |
| 263 | return true; |
| 264 | } |
| 265 | |
| 266 | // static |
| 267 | void ExternalExtensionProviderImpl::CreateExternalProviders( |
| 268 | VisitorInterface* service, |
| 269 | Profile* profile, |
| 270 | ProviderCollection* provider_list) { |
[email protected] | 73e4c36 | 2011-09-22 14:47:18 | [diff] [blame] | 271 | |
| 272 | // On Mac OS, items in /Library/... should be written by the superuser. |
| 273 | // Check that all components of the path are writable by root only. |
| 274 | ExternalPrefExtensionLoader::Options options; |
| 275 | #if defined(OS_MACOSX) |
| 276 | options = ExternalPrefExtensionLoader::ENSURE_PATH_CONTROLLED_BY_ADMIN; |
| 277 | #else |
| 278 | options = ExternalPrefExtensionLoader::NONE; |
| 279 | #endif |
| 280 | |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 281 | provider_list->push_back( |
| 282 | linked_ptr<ExternalExtensionProviderInterface>( |
| 283 | new ExternalExtensionProviderImpl( |
| 284 | service, |
[email protected] | a29a517a | 2011-01-21 21:11:12 | [diff] [blame] | 285 | new ExternalPrefExtensionLoader( |
[email protected] | 73e4c36 | 2011-09-22 14:47:18 | [diff] [blame] | 286 | chrome::DIR_EXTERNAL_EXTENSIONS, options), |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 287 | Extension::EXTERNAL_PREF, |
| 288 | Extension::EXTERNAL_PREF_DOWNLOAD))); |
[email protected] | a29a517a | 2011-01-21 21:11:12 | [diff] [blame] | 289 | |
[email protected] | 73e4c36 | 2011-09-22 14:47:18 | [diff] [blame] | 290 | #if defined(OS_MACOSX) |
| 291 | // Support old path to external extensions file as we migrate to the |
| 292 | // new one. See crbug/67203. |
| 293 | provider_list->push_back( |
| 294 | linked_ptr<ExternalExtensionProviderInterface>( |
| 295 | new ExternalExtensionProviderImpl( |
| 296 | service, |
| 297 | new ExternalPrefExtensionLoader( |
[email protected] | 23b0097 | 2011-10-04 17:17:26 | [diff] [blame] | 298 | chrome::DIR_DEPRECATED_EXTERNAL_EXTENSIONS, |
[email protected] | 73e4c36 | 2011-09-22 14:47:18 | [diff] [blame] | 299 | ExternalPrefExtensionLoader::NONE), |
| 300 | Extension::EXTERNAL_PREF, |
| 301 | Extension::EXTERNAL_PREF_DOWNLOAD))); |
| 302 | #endif |
| 303 | |
[email protected] | a29a517a | 2011-01-21 21:11:12 | [diff] [blame] | 304 | #if defined(OS_CHROMEOS) |
| 305 | // Chrome OS specific source for OEM customization. |
| 306 | provider_list->push_back( |
| 307 | linked_ptr<ExternalExtensionProviderInterface>( |
| 308 | new ExternalExtensionProviderImpl( |
| 309 | service, |
| 310 | new ExternalPrefExtensionLoader( |
[email protected] | 998e8b9 | 2011-09-22 15:07:24 | [diff] [blame] | 311 | chrome::DIR_USER_EXTERNAL_EXTENSIONS, |
| 312 | ExternalPrefExtensionLoader::NONE), |
[email protected] | a29a517a | 2011-01-21 21:11:12 | [diff] [blame] | 313 | Extension::EXTERNAL_PREF, |
| 314 | Extension::EXTERNAL_PREF_DOWNLOAD))); |
| 315 | #endif |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 316 | #if defined(OS_WIN) |
| 317 | provider_list->push_back( |
| 318 | linked_ptr<ExternalExtensionProviderInterface>( |
| 319 | new ExternalExtensionProviderImpl( |
| 320 | service, |
| 321 | new ExternalRegistryExtensionLoader, |
| 322 | Extension::EXTERNAL_REGISTRY, |
| 323 | Extension::INVALID))); |
| 324 | #endif |
| 325 | provider_list->push_back( |
| 326 | linked_ptr<ExternalExtensionProviderInterface>( |
| 327 | new ExternalExtensionProviderImpl( |
| 328 | service, |
| 329 | new ExternalPolicyExtensionLoader(profile), |
| 330 | Extension::INVALID, |
| 331 | Extension::EXTERNAL_POLICY_DOWNLOAD))); |
[email protected] | 51a7a9d | 2011-09-27 17:21:41 | [diff] [blame] | 332 | |
[email protected] | 98b4aca6 | 2011-09-28 01:27:23 | [diff] [blame] | 333 | #if !defined(OS_CHROMEOS) |
[email protected] | 51a7a9d | 2011-09-27 17:21:41 | [diff] [blame] | 334 | // Install default apps, except for the experimental group of users that are |
| 335 | // to be excluded. |
| 336 | static bool install_apps = !base::FieldTrialList::TrialExists( |
| 337 | kDefaultAppsTrial_Name) || (base::FieldTrialList::Find( |
| 338 | kDefaultAppsTrial_Name)->group_name() != |
| 339 | kDefaultAppsTrial_NoAppsGroup); |
| 340 | if (install_apps) { |
[email protected] | e601e82 | 2011-10-05 19:25:37 | [diff] [blame^] | 341 | // Don't bother installing default apps in locales where its known that |
| 342 | // they don't work. |
| 343 | // TODO(rogerta): Do this check dynamically once the webstore can expose |
| 344 | // an API. |
| 345 | const std::string& locale = g_browser_process->GetApplicationLocale(); |
| 346 | static const char* unsupported_locales[] = {"CN", "TR", "IR"}; |
| 347 | bool supported_locale = true; |
| 348 | for (size_t i = 0; i < arraysize(unsupported_locales); ++i) { |
| 349 | if (EndsWith(locale, unsupported_locales[i], false)) { |
| 350 | supported_locale = false; |
| 351 | break; |
| 352 | } |
| 353 | } |
| 354 | |
| 355 | if (supported_locale) { |
| 356 | provider_list->push_back( |
| 357 | linked_ptr<ExternalExtensionProviderInterface>( |
| 358 | new DefaultAppsProvider(service, profile))); |
| 359 | } |
[email protected] | 51a7a9d | 2011-09-27 17:21:41 | [diff] [blame] | 360 | } |
[email protected] | 98b4aca6 | 2011-09-28 01:27:23 | [diff] [blame] | 361 | #endif |
[email protected] | 8e4560b6 | 2011-01-14 10:09:14 | [diff] [blame] | 362 | } |