1 // Copyright (c) 2012 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/startup_helper.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/command_line.h"
10 #include "base/files/file_path.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/run_loop.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "chrome/browser/extensions/extension_service.h"
17 #include "chrome/browser/extensions/sandboxed_unpacker.h"
18 #include "chrome/browser/extensions/webstore_startup_installer.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/common/chrome_switches.h"
21 #include "chrome/common/extensions/chrome_extensions_client.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/web_contents.h"
24 #include "extensions/common/extension.h"
25 #include "ipc/ipc_message.h"
26
27 using content::BrowserThread;
28
29 namespace {
30
PrintPackExtensionMessage(const std::string & message)31 void PrintPackExtensionMessage(const std::string& message) {
32 printf("%s\n", message.c_str());
33 }
34
35 } // namespace
36
37 namespace extensions {
38
StartupHelper()39 StartupHelper::StartupHelper() : pack_job_succeeded_(false) {
40 ExtensionsClient::Set(ChromeExtensionsClient::GetInstance());
41 }
42
OnPackSuccess(const base::FilePath & crx_path,const base::FilePath & output_private_key_path)43 void StartupHelper::OnPackSuccess(
44 const base::FilePath& crx_path,
45 const base::FilePath& output_private_key_path) {
46 pack_job_succeeded_ = true;
47 PrintPackExtensionMessage(
48 base::UTF16ToUTF8(
49 PackExtensionJob::StandardSuccessMessage(crx_path,
50 output_private_key_path)));
51 }
52
OnPackFailure(const std::string & error_message,ExtensionCreator::ErrorType type)53 void StartupHelper::OnPackFailure(const std::string& error_message,
54 ExtensionCreator::ErrorType type) {
55 PrintPackExtensionMessage(error_message);
56 }
57
PackExtension(const CommandLine & cmd_line)58 bool StartupHelper::PackExtension(const CommandLine& cmd_line) {
59 if (!cmd_line.HasSwitch(switches::kPackExtension))
60 return false;
61
62 // Input Paths.
63 base::FilePath src_dir =
64 cmd_line.GetSwitchValuePath(switches::kPackExtension);
65 base::FilePath private_key_path;
66 if (cmd_line.HasSwitch(switches::kPackExtensionKey)) {
67 private_key_path = cmd_line.GetSwitchValuePath(switches::kPackExtensionKey);
68 }
69
70 // Launch a job to perform the packing on the file thread. Ignore warnings
71 // from the packing process. (e.g. Overwrite any existing crx file.)
72 pack_job_ = new PackExtensionJob(this, src_dir, private_key_path,
73 ExtensionCreator::kOverwriteCRX);
74 pack_job_->set_asynchronous(false);
75 pack_job_->Start();
76
77 return pack_job_succeeded_;
78 }
79
80 namespace {
81
82 class ValidateCrxHelper : public SandboxedUnpackerClient {
83 public:
ValidateCrxHelper(const base::FilePath & crx_file,const base::FilePath & temp_dir,base::RunLoop * run_loop)84 ValidateCrxHelper(const base::FilePath& crx_file,
85 const base::FilePath& temp_dir,
86 base::RunLoop* run_loop)
87 : crx_file_(crx_file), temp_dir_(temp_dir), run_loop_(run_loop),
88 finished_(false), success_(false) {}
89
finished()90 bool finished() { return finished_; }
success()91 bool success() { return success_; }
error()92 const base::string16& error() { return error_; }
93
Start()94 void Start() {
95 BrowserThread::PostTask(BrowserThread::FILE,
96 FROM_HERE,
97 base::Bind(&ValidateCrxHelper::StartOnFileThread,
98 this));
99 }
100
101 protected:
~ValidateCrxHelper()102 virtual ~ValidateCrxHelper() {}
103
OnUnpackSuccess(const base::FilePath & temp_dir,const base::FilePath & extension_root,const base::DictionaryValue * original_manifest,const Extension * extension,const SkBitmap & install_icon)104 virtual void OnUnpackSuccess(const base::FilePath& temp_dir,
105 const base::FilePath& extension_root,
106 const base::DictionaryValue* original_manifest,
107 const Extension* extension,
108 const SkBitmap& install_icon) OVERRIDE {
109 finished_ = true;
110 success_ = true;
111 BrowserThread::PostTask(BrowserThread::UI,
112 FROM_HERE,
113 base::Bind(&ValidateCrxHelper::FinishOnUIThread,
114 this));
115 }
116
OnUnpackFailure(const base::string16 & error)117 virtual void OnUnpackFailure(const base::string16& error) OVERRIDE {
118 finished_ = true;
119 success_ = false;
120 error_ = error;
121 BrowserThread::PostTask(BrowserThread::UI,
122 FROM_HERE,
123 base::Bind(&ValidateCrxHelper::FinishOnUIThread,
124 this));
125 }
126
FinishOnUIThread()127 void FinishOnUIThread() {
128 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
129 if (run_loop_->running())
130 run_loop_->Quit();
131 }
132
StartOnFileThread()133 void StartOnFileThread() {
134 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
135 scoped_refptr<base::MessageLoopProxy> file_thread_proxy =
136 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE);
137
138 scoped_refptr<SandboxedUnpacker> unpacker(
139 new SandboxedUnpacker(crx_file_,
140 Manifest::INTERNAL,
141 0, /* no special creation flags */
142 temp_dir_,
143 file_thread_proxy.get(),
144 this));
145 unpacker->Start();
146 }
147
148 // The file being validated.
149 const base::FilePath& crx_file_;
150
151 // The temporary directory where the sandboxed unpacker will do work.
152 const base::FilePath& temp_dir_;
153
154 // Unowned pointer to a runloop, so our consumer can wait for us to finish.
155 base::RunLoop* run_loop_;
156
157 // Whether we're finished unpacking;
158 bool finished_;
159
160 // Whether the unpacking was successful.
161 bool success_;
162
163 // If the unpacking wasn't successful, this contains an error message.
164 base::string16 error_;
165 };
166
167 } // namespace
168
ValidateCrx(const CommandLine & cmd_line,std::string * error)169 bool StartupHelper::ValidateCrx(const CommandLine& cmd_line,
170 std::string* error) {
171 CHECK(error);
172 base::FilePath path = cmd_line.GetSwitchValuePath(switches::kValidateCrx);
173 if (path.empty()) {
174 *error = base::StringPrintf("Empty path passed for %s",
175 switches::kValidateCrx);
176 return false;
177 }
178 base::ScopedTempDir temp_dir;
179
180 if (!temp_dir.CreateUniqueTempDir()) {
181 *error = std::string("Failed to create temp dir");
182 return false;
183 }
184
185 base::RunLoop run_loop;
186 scoped_refptr<ValidateCrxHelper> helper(
187 new ValidateCrxHelper(path, temp_dir.path(), &run_loop));
188 helper->Start();
189 if (!helper->finished())
190 run_loop.Run();
191
192 bool success = helper->success();
193 if (!success)
194 *error = base::UTF16ToUTF8(helper->error());
195 return success;
196 }
197
198 namespace {
199
200 class AppInstallHelper {
201 public:
202 // A callback for when the install process is done.
203 typedef base::Callback<void()> DoneCallback;
204
205 AppInstallHelper();
206 virtual ~AppInstallHelper();
success()207 bool success() { return success_; }
error()208 const std::string& error() { return error_; }
209 void BeginInstall(Profile* profile,
210 const std::string& id,
211 bool show_prompt,
212 DoneCallback callback);
213
214 private:
215 WebstoreStandaloneInstaller::Callback Callback();
216 void OnAppInstallComplete(bool success, const std::string& error);
217
218 DoneCallback done_callback_;
219
220 // These hold on to the result of the app install when it is complete.
221 bool success_;
222 std::string error_;
223
224 scoped_refptr<WebstoreStandaloneInstaller> installer_;
225 };
226
AppInstallHelper()227 AppInstallHelper::AppInstallHelper() : success_(false) {}
228
~AppInstallHelper()229 AppInstallHelper::~AppInstallHelper() {}
230
Callback()231 WebstoreStandaloneInstaller::Callback AppInstallHelper::Callback() {
232 return base::Bind(&AppInstallHelper::OnAppInstallComplete,
233 base::Unretained(this));
234 }
235
BeginInstall(Profile * profile,const std::string & id,bool show_prompt,DoneCallback done_callback)236 void AppInstallHelper::BeginInstall(
237 Profile* profile,
238 const std::string& id,
239 bool show_prompt,
240 DoneCallback done_callback) {
241 done_callback_ = done_callback;
242
243 WebstoreStandaloneInstaller::Callback callback =
244 base::Bind(&AppInstallHelper::OnAppInstallComplete,
245 base::Unretained(this));
246 installer_ = new WebstoreStartupInstaller(
247 id,
248 profile,
249 show_prompt,
250 callback);
251 installer_->BeginInstall();
252 }
253
OnAppInstallComplete(bool success,const std::string & error)254 void AppInstallHelper::OnAppInstallComplete(bool success,
255 const std::string& error) {
256 success_ = success;
257 error_= error;
258 done_callback_.Run();
259 }
260
DeleteHelperAndRunCallback(AppInstallHelper * helper,base::Callback<void ()> callback)261 void DeleteHelperAndRunCallback(AppInstallHelper* helper,
262 base::Callback<void()> callback) {
263 delete helper;
264 callback.Run();
265 }
266
267 } // namespace
268
InstallFromWebstore(const CommandLine & cmd_line,Profile * profile)269 bool StartupHelper::InstallFromWebstore(const CommandLine& cmd_line,
270 Profile* profile) {
271 std::string id = cmd_line.GetSwitchValueASCII(switches::kInstallFromWebstore);
272 if (!Extension::IdIsValid(id)) {
273 LOG(ERROR) << "Invalid id for " << switches::kInstallFromWebstore
274 << " : '" << id << "'";
275 return false;
276 }
277
278 AppInstallHelper helper;
279 base::RunLoop run_loop;
280 helper.BeginInstall(profile, id, true, run_loop.QuitClosure());
281 run_loop.Run();
282
283 if (!helper.success())
284 LOG(ERROR) << "InstallFromWebstore failed with error: " << helper.error();
285 return helper.success();
286 }
287
LimitedInstallFromWebstore(const CommandLine & cmd_line,Profile * profile,base::Callback<void ()> done_callback)288 void StartupHelper::LimitedInstallFromWebstore(
289 const CommandLine& cmd_line,
290 Profile* profile,
291 base::Callback<void()> done_callback) {
292 std::string id = WebStoreIdFromLimitedInstallCmdLine(cmd_line);
293 if (!Extension::IdIsValid(id)) {
294 LOG(ERROR) << "Invalid index for " << switches::kLimitedInstallFromWebstore;
295 done_callback.Run();
296 return;
297 }
298
299 AppInstallHelper* helper = new AppInstallHelper();
300 helper->BeginInstall(profile, id, false /*show_prompt*/,
301 base::Bind(&DeleteHelperAndRunCallback,
302 helper, done_callback));
303 }
304
WebStoreIdFromLimitedInstallCmdLine(const CommandLine & cmd_line)305 std::string StartupHelper::WebStoreIdFromLimitedInstallCmdLine(
306 const CommandLine& cmd_line) {
307 std::string index = cmd_line.GetSwitchValueASCII(
308 switches::kLimitedInstallFromWebstore);
309 std::string id;
310 if (index == "1") {
311 id = "nckgahadagoaajjgafhacjanaoiihapd";
312 } else if (index == "2") {
313 id = "ecglahbcnmdpdciemllbhojghbkagdje";
314 }
315 return id;
316 }
317
~StartupHelper()318 StartupHelper::~StartupHelper() {
319 if (pack_job_.get())
320 pack_job_->ClearClient();
321 }
322
323 } // namespace extensions
324