blob: 38d1417d81da7c8c748804c4bc823fd618533674 [file] [log] [blame]
[email protected]8e4560b62011-01-14 10:09:141// 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]8e4560b62011-01-14 10:09:147#include "base/file_path.h"
8#include "base/logging.h"
[email protected]3b63f8f42011-03-28 01:54:159#include "base/memory/linked_ptr.h"
[email protected]51a7a9d2011-09-27 17:21:4110#include "base/metrics/field_trial.h"
[email protected]8e4560b62011-01-14 10:09:1411#include "base/path_service.h"
[email protected]e601e822011-10-05 19:25:3712#include "base/string_util.h"
[email protected]8e4560b62011-01-14 10:09:1413#include "base/values.h"
14#include "base/version.h"
[email protected]e601e822011-10-05 19:25:3715#include "chrome/browser/browser_process.h"
[email protected]51a7a9d2011-09-27 17:21:4116#include "chrome/browser/extensions/default_apps_trial.h"
17#include "chrome/browser/extensions/extension_service.h"
[email protected]8e4560b62011-01-14 10:09:1418#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]f0841cd2011-01-19 15:07:2422#include "chrome/common/chrome_paths.h"
[email protected]5f945a0e2011-03-01 17:47:5323#include "content/browser/browser_thread.h"
[email protected]8e4560b62011-01-14 10:09:1424
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.
30const char ExternalExtensionProviderImpl::kLocation[] = "location";
31const char ExternalExtensionProviderImpl::kState[] = "state";
32const char ExternalExtensionProviderImpl::kExternalCrx[] = "external_crx";
33const char ExternalExtensionProviderImpl::kExternalVersion[] =
34 "external_version";
35const char ExternalExtensionProviderImpl::kExternalUpdateUrl[] =
36 "external_update_url";
37
[email protected]98b4aca62011-09-28 01:27:2338#if !defined(OS_CHROMEOS)
[email protected]51a7a9d2011-09-27 17:21:4139class 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
60void DefaultAppsProvider::ServiceShutdown() {
61 profile_ = NULL;
62 ExternalExtensionProviderImpl::ServiceShutdown();
63}
64
65void 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]98b4aca62011-09-28 01:27:2377#endif
[email protected]51a7a9d2011-09-27 17:21:4178
[email protected]8e4560b62011-01-14 10:09:1479ExternalExtensionProviderImpl::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
93ExternalExtensionProviderImpl::~ExternalExtensionProviderImpl() {
94 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
95 loader_->OwnerShutdown();
96}
97
98void ExternalExtensionProviderImpl::VisitRegisteredExtension() const {
99 // The loader will call back to SetPrefs.
100 loader_->StartLoading();
101}
102
103void 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]ab22ba42011-01-14 16:36:38118
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]8e4560b62011-01-14 10:09:14122 continue;
[email protected]ab22ba42011-01-14 16:36:38123 }
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]8e4560b62011-01-14 10:09:14131
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]f0841cd2011-01-19 15:07:24169 // If the path is relative, and the provider has a base path,
170 // build the absolute path to the crx file.
[email protected]8e4560b62011-01-14 10:09:14171 FilePath path(external_crx);
172 if (!path.IsAbsolute()) {
[email protected]f0841cd2011-01-19 15:07:24173 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]8e4560b62011-01-14 10:09:14179 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]a29a517a2011-01-21 21:11:12191 crx_location_);
[email protected]8e4560b62011-01-14 10:09:14192 } 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]ab22ba42011-01-14 16:36:38202 << extension_id.c_str() << ". Key " << kExternalUpdateUrl
203 << " has value \"" << external_update_url
204 << "\", which is not a valid URL.";
[email protected]8e4560b62011-01-14 10:09:14205 continue;
206 }
207 service_->OnExternalExtensionUpdateUrlFound(
208 extension_id, update_url, download_location_);
209 }
210 }
211
212 service_->OnExternalProviderReady();
213}
214
215void ExternalExtensionProviderImpl::ServiceShutdown() {
216 service_ = NULL;
217}
218
219bool ExternalExtensionProviderImpl::IsReady() {
220 return ready_;
221}
222
223bool 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
231bool 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
267void ExternalExtensionProviderImpl::CreateExternalProviders(
268 VisitorInterface* service,
269 Profile* profile,
270 ProviderCollection* provider_list) {
[email protected]73e4c362011-09-22 14:47:18271
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]8e4560b62011-01-14 10:09:14281 provider_list->push_back(
282 linked_ptr<ExternalExtensionProviderInterface>(
283 new ExternalExtensionProviderImpl(
284 service,
[email protected]a29a517a2011-01-21 21:11:12285 new ExternalPrefExtensionLoader(
[email protected]73e4c362011-09-22 14:47:18286 chrome::DIR_EXTERNAL_EXTENSIONS, options),
[email protected]8e4560b62011-01-14 10:09:14287 Extension::EXTERNAL_PREF,
288 Extension::EXTERNAL_PREF_DOWNLOAD)));
[email protected]a29a517a2011-01-21 21:11:12289
[email protected]73e4c362011-09-22 14:47:18290#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]23b00972011-10-04 17:17:26298 chrome::DIR_DEPRECATED_EXTERNAL_EXTENSIONS,
[email protected]73e4c362011-09-22 14:47:18299 ExternalPrefExtensionLoader::NONE),
300 Extension::EXTERNAL_PREF,
301 Extension::EXTERNAL_PREF_DOWNLOAD)));
302#endif
303
[email protected]a29a517a2011-01-21 21:11:12304#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]998e8b92011-09-22 15:07:24311 chrome::DIR_USER_EXTERNAL_EXTENSIONS,
312 ExternalPrefExtensionLoader::NONE),
[email protected]a29a517a2011-01-21 21:11:12313 Extension::EXTERNAL_PREF,
314 Extension::EXTERNAL_PREF_DOWNLOAD)));
315#endif
[email protected]8e4560b62011-01-14 10:09:14316#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]51a7a9d2011-09-27 17:21:41332
[email protected]98b4aca62011-09-28 01:27:23333#if !defined(OS_CHROMEOS)
[email protected]51a7a9d2011-09-27 17:21:41334 // 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]e601e822011-10-05 19:25:37341 // 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]51a7a9d2011-09-27 17:21:41360 }
[email protected]98b4aca62011-09-28 01:27:23361#endif
[email protected]8e4560b62011-01-14 10:09:14362}