1// Copyright (c) 2013 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/unpacked_installer.h"
6
7#include "base/bind.h"
8#include "base/callback.h"
9#include "base/files/file_util.h"
10#include "base/strings/string_util.h"
11#include "base/threading/thread_restrictions.h"
12#include "chrome/browser/extensions/extension_error_reporter.h"
13#include "chrome/browser/extensions/extension_install_prompt.h"
14#include "chrome/browser/extensions/extension_install_ui.h"
15#include "chrome/browser/extensions/extension_management.h"
16#include "chrome/browser/extensions/extension_service.h"
17#include "chrome/browser/extensions/permissions_updater.h"
18#include "chrome/browser/profiles/profile.h"
19#include "chrome/common/extensions/api/plugins/plugins_handler.h"
20#include "components/crx_file/id_util.h"
21#include "content/public/browser/browser_thread.h"
22#include "extensions/browser/extension_prefs.h"
23#include "extensions/browser/extension_registry.h"
24#include "extensions/browser/install_flag.h"
25#include "extensions/common/extension.h"
26#include "extensions/common/extension_l10n_util.h"
27#include "extensions/common/file_util.h"
28#include "extensions/common/manifest.h"
29#include "sync/api/string_ordinal.h"
30
31using content::BrowserThread;
32using extensions::Extension;
33
34namespace {
35
36const char kUnpackedExtensionsBlacklistedError[] =
37    "Loading of unpacked extensions is disabled by the administrator.";
38
39// Manages an ExtensionInstallPrompt for a particular extension.
40class SimpleExtensionLoadPrompt : public ExtensionInstallPrompt::Delegate {
41 public:
42  SimpleExtensionLoadPrompt(const Extension* extension,
43                            Profile* profile,
44                            const base::Closure& callback);
45  virtual ~SimpleExtensionLoadPrompt();
46
47  void ShowPrompt();
48
49  // ExtensionInstallUI::Delegate
50  virtual void InstallUIProceed() OVERRIDE;
51  virtual void InstallUIAbort(bool user_initiated) OVERRIDE;
52
53 private:
54  scoped_ptr<ExtensionInstallPrompt> install_ui_;
55  scoped_refptr<const Extension> extension_;
56  base::Closure callback_;
57};
58
59SimpleExtensionLoadPrompt::SimpleExtensionLoadPrompt(
60    const Extension* extension,
61    Profile* profile,
62    const base::Closure& callback)
63    : install_ui_(ExtensionInstallUI::CreateInstallPromptWithProfile(
64          profile)),
65      extension_(extension),
66      callback_(callback) {
67}
68
69SimpleExtensionLoadPrompt::~SimpleExtensionLoadPrompt() {
70}
71
72void SimpleExtensionLoadPrompt::ShowPrompt() {
73  switch (ExtensionInstallPrompt::g_auto_confirm_for_tests) {
74    case ExtensionInstallPrompt::NONE:
75      install_ui_->ConfirmInstall(
76          this,
77          extension_.get(),
78          ExtensionInstallPrompt::GetDefaultShowDialogCallback());
79      break;
80    case ExtensionInstallPrompt::ACCEPT:
81      InstallUIProceed();
82      break;
83    case ExtensionInstallPrompt::CANCEL:
84      InstallUIAbort(false);
85  }
86}
87
88void SimpleExtensionLoadPrompt::InstallUIProceed() {
89  callback_.Run();
90  delete this;
91}
92
93void SimpleExtensionLoadPrompt::InstallUIAbort(bool user_initiated) {
94  delete this;
95}
96
97}  // namespace
98
99namespace extensions {
100
101// static
102scoped_refptr<UnpackedInstaller> UnpackedInstaller::Create(
103    ExtensionService* extension_service) {
104  DCHECK(extension_service);
105  return scoped_refptr<UnpackedInstaller>(
106      new UnpackedInstaller(extension_service));
107}
108
109UnpackedInstaller::UnpackedInstaller(ExtensionService* extension_service)
110    : service_weak_(extension_service->AsWeakPtr()),
111      prompt_for_plugins_(true),
112      require_modern_manifest_version_(true),
113      be_noisy_on_failure_(true),
114      install_checker_(extension_service->profile()) {
115  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
116}
117
118UnpackedInstaller::~UnpackedInstaller() {
119  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
120        BrowserThread::CurrentlyOn(BrowserThread::FILE));
121}
122
123void UnpackedInstaller::Load(const base::FilePath& path_in) {
124  DCHECK(extension_path_.empty());
125  extension_path_ = path_in;
126  BrowserThread::PostTask(
127      BrowserThread::FILE,
128      FROM_HERE,
129      base::Bind(&UnpackedInstaller::GetAbsolutePath, this));
130}
131
132bool UnpackedInstaller::LoadFromCommandLine(const base::FilePath& path_in,
133                                            std::string* extension_id) {
134  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
135  DCHECK(extension_path_.empty());
136
137  if (!service_weak_.get())
138    return false;
139  // Load extensions from the command line synchronously to avoid a race
140  // between extension loading and loading an URL from the command line.
141  base::ThreadRestrictions::ScopedAllowIO allow_io;
142
143  extension_path_ = base::MakeAbsoluteFilePath(path_in);
144
145  if (!IsLoadingUnpackedAllowed()) {
146    ReportExtensionLoadError(kUnpackedExtensionsBlacklistedError);
147    return false;
148  }
149
150  std::string error;
151  install_checker_.set_extension(
152      file_util::LoadExtension(
153          extension_path_, Manifest::COMMAND_LINE, GetFlags(), &error).get());
154
155  if (!extension() ||
156      !extension_l10n_util::ValidateExtensionLocales(
157          extension_path_, extension()->manifest()->value(), &error)) {
158    ReportExtensionLoadError(error);
159    return false;
160  }
161
162  PermissionsUpdater(
163      service_weak_->profile(), PermissionsUpdater::INIT_FLAG_TRANSIENT)
164      .InitializePermissions(extension());
165  ShowInstallPrompt();
166
167  *extension_id = extension()->id();
168  return true;
169}
170
171void UnpackedInstaller::ShowInstallPrompt() {
172  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
173  if (!service_weak_.get())
174    return;
175
176  const ExtensionSet& disabled_extensions =
177      ExtensionRegistry::Get(service_weak_->profile())->disabled_extensions();
178  if (service_weak_->show_extensions_prompts() && prompt_for_plugins_ &&
179      PluginInfo::HasPlugins(extension()) &&
180      !disabled_extensions.Contains(extension()->id())) {
181    SimpleExtensionLoadPrompt* prompt = new SimpleExtensionLoadPrompt(
182        extension(),
183        install_checker_.profile(),
184        base::Bind(&UnpackedInstaller::StartInstallChecks, this));
185    prompt->ShowPrompt();
186    return;
187  }
188  StartInstallChecks();
189}
190
191void UnpackedInstaller::StartInstallChecks() {
192  install_checker_.Start(
193      ExtensionInstallChecker::CHECK_REQUIREMENTS |
194          ExtensionInstallChecker::CHECK_MANAGEMENT_POLICY,
195      true /* fail fast */,
196      base::Bind(&UnpackedInstaller::OnInstallChecksComplete, this));
197}
198
199void UnpackedInstaller::OnInstallChecksComplete(int failed_checks) {
200  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
201
202  if (!install_checker_.policy_error().empty()) {
203    ReportExtensionLoadError(install_checker_.policy_error());
204    return;
205  }
206
207  if (!install_checker_.requirement_errors().empty()) {
208    ReportExtensionLoadError(
209        JoinString(install_checker_.requirement_errors(), ' '));
210    return;
211  }
212
213  InstallExtension();
214}
215
216int UnpackedInstaller::GetFlags() {
217  std::string id = crx_file::id_util::GenerateIdForPath(extension_path_);
218  bool allow_file_access =
219      Manifest::ShouldAlwaysAllowFileAccess(Manifest::UNPACKED);
220  ExtensionPrefs* prefs = ExtensionPrefs::Get(service_weak_->profile());
221  if (prefs->HasAllowFileAccessSetting(id))
222    allow_file_access = prefs->AllowFileAccess(id);
223
224  int result = Extension::FOLLOW_SYMLINKS_ANYWHERE;
225  if (allow_file_access)
226    result |= Extension::ALLOW_FILE_ACCESS;
227  if (require_modern_manifest_version_)
228    result |= Extension::REQUIRE_MODERN_MANIFEST_VERSION;
229
230  return result;
231}
232
233bool UnpackedInstaller::IsLoadingUnpackedAllowed() const {
234  if (!service_weak_.get())
235    return true;
236  // If there is a "*" in the extension blacklist, then no extensions should be
237  // allowed at all (except explicitly whitelisted extensions).
238  return !ExtensionManagementFactory::GetForBrowserContext(
239              service_weak_->profile())->BlacklistedByDefault();
240}
241
242void UnpackedInstaller::GetAbsolutePath() {
243  CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
244
245  extension_path_ = base::MakeAbsoluteFilePath(extension_path_);
246
247  std::string error;
248  if (!file_util::CheckForIllegalFilenames(extension_path_, &error)) {
249    BrowserThread::PostTask(
250        BrowserThread::UI,
251        FROM_HERE,
252        base::Bind(&UnpackedInstaller::ReportExtensionLoadError, this, error));
253    return;
254  }
255  BrowserThread::PostTask(
256      BrowserThread::UI, FROM_HERE,
257      base::Bind(&UnpackedInstaller::CheckExtensionFileAccess, this));
258}
259
260void UnpackedInstaller::CheckExtensionFileAccess() {
261  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
262  if (!service_weak_.get())
263    return;
264
265  if (!IsLoadingUnpackedAllowed()) {
266    ReportExtensionLoadError(kUnpackedExtensionsBlacklistedError);
267    return;
268  }
269
270  BrowserThread::PostTask(
271      BrowserThread::FILE,
272      FROM_HERE,
273      base::Bind(&UnpackedInstaller::LoadWithFileAccess, this, GetFlags()));
274}
275
276void UnpackedInstaller::LoadWithFileAccess(int flags) {
277  CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
278
279  std::string error;
280  install_checker_.set_extension(
281      file_util::LoadExtension(
282          extension_path_, Manifest::UNPACKED, flags, &error).get());
283
284  if (!extension() ||
285      !extension_l10n_util::ValidateExtensionLocales(
286          extension_path_, extension()->manifest()->value(), &error)) {
287    BrowserThread::PostTask(
288        BrowserThread::UI,
289        FROM_HERE,
290        base::Bind(&UnpackedInstaller::ReportExtensionLoadError, this, error));
291    return;
292  }
293
294  BrowserThread::PostTask(
295      BrowserThread::UI,
296      FROM_HERE,
297      base::Bind(&UnpackedInstaller::ShowInstallPrompt, this));
298}
299
300void UnpackedInstaller::ReportExtensionLoadError(const std::string &error) {
301  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
302
303  if (service_weak_.get()) {
304    ExtensionErrorReporter::GetInstance()->ReportLoadError(
305        extension_path_,
306        error,
307        service_weak_->profile(),
308        be_noisy_on_failure_);
309  }
310}
311
312void UnpackedInstaller::InstallExtension() {
313  DCHECK_CURRENTLY_ON(BrowserThread::UI);
314
315  PermissionsUpdater perms_updater(service_weak_->profile());
316  perms_updater.InitializePermissions(extension());
317  perms_updater.GrantActivePermissions(extension());
318
319  service_weak_->OnExtensionInstalled(
320      extension(), syncer::StringOrdinal(), kInstallFlagInstallImmediately);
321}
322
323}  // namespace extensions
324