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 31 void PrintPackExtensionMessage(const std::string& message) { 32 printf("%s\n", message.c_str()); 33 } 34 35 } // namespace 36 37 namespace extensions { 38 39 StartupHelper::StartupHelper() : pack_job_succeeded_(false) { 40 ExtensionsClient::Set(ChromeExtensionsClient::GetInstance()); 41 } 42 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 UTF16ToUTF8( 49 PackExtensionJob::StandardSuccessMessage(crx_path, 50 output_private_key_path))); 51 } 52 53 void StartupHelper::OnPackFailure(const std::string& error_message, 54 ExtensionCreator::ErrorType type) { 55 PrintPackExtensionMessage(error_message); 56 } 57 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: 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 90 bool finished() { return finished_; } 91 bool success() { return success_; } 92 const base::string16& error() { return error_; } 93 94 void Start() { 95 BrowserThread::PostTask(BrowserThread::FILE, 96 FROM_HERE, 97 base::Bind(&ValidateCrxHelper::StartOnFileThread, 98 this)); 99 } 100 101 protected: 102 virtual ~ValidateCrxHelper() {} 103 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 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 127 void FinishOnUIThread() { 128 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 129 if (run_loop_->running()) 130 run_loop_->Quit(); 131 } 132 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 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 = UTF16ToUTF8(helper->error()); 195 return success; 196 } 197 198 bool StartupHelper::UninstallExtension(const CommandLine& cmd_line, 199 Profile* profile) { 200 DCHECK(profile); 201 202 if (!cmd_line.HasSwitch(switches::kUninstallExtension)) 203 return false; 204 205 ExtensionService* extension_service = profile->GetExtensionService(); 206 if (!extension_service) 207 return false; 208 209 std::string extension_id = cmd_line.GetSwitchValueASCII( 210 switches::kUninstallExtension); 211 return ExtensionService::UninstallExtensionHelper(extension_service, 212 extension_id); 213 } 214 215 namespace { 216 217 class AppInstallHelper { 218 public: 219 // A callback for when the install process is done. 220 typedef base::Callback<void()> DoneCallback; 221 222 AppInstallHelper(); 223 virtual ~AppInstallHelper(); 224 bool success() { return success_; } 225 const std::string& error() { return error_; } 226 void BeginInstall(Profile* profile, 227 const std::string& id, 228 bool show_prompt, 229 DoneCallback callback); 230 231 private: 232 WebstoreStandaloneInstaller::Callback Callback(); 233 void OnAppInstallComplete(bool success, const std::string& error); 234 235 DoneCallback done_callback_; 236 237 // These hold on to the result of the app install when it is complete. 238 bool success_; 239 std::string error_; 240 241 scoped_refptr<WebstoreStandaloneInstaller> installer_; 242 }; 243 244 AppInstallHelper::AppInstallHelper() : success_(false) {} 245 246 AppInstallHelper::~AppInstallHelper() {} 247 248 WebstoreStandaloneInstaller::Callback AppInstallHelper::Callback() { 249 return base::Bind(&AppInstallHelper::OnAppInstallComplete, 250 base::Unretained(this)); 251 } 252 253 void AppInstallHelper::BeginInstall( 254 Profile* profile, 255 const std::string& id, 256 bool show_prompt, 257 DoneCallback done_callback) { 258 done_callback_ = done_callback; 259 260 WebstoreStandaloneInstaller::Callback callback = 261 base::Bind(&AppInstallHelper::OnAppInstallComplete, 262 base::Unretained(this)); 263 installer_ = new WebstoreStartupInstaller( 264 id, 265 profile, 266 show_prompt, 267 callback); 268 installer_->BeginInstall(); 269 } 270 271 void AppInstallHelper::OnAppInstallComplete(bool success, 272 const std::string& error) { 273 success_ = success; 274 error_= error; 275 done_callback_.Run(); 276 } 277 278 void DeleteHelperAndRunCallback(AppInstallHelper* helper, 279 base::Callback<void()> callback) { 280 delete helper; 281 callback.Run(); 282 } 283 284 } // namespace 285 286 bool StartupHelper::InstallFromWebstore(const CommandLine& cmd_line, 287 Profile* profile) { 288 std::string id = cmd_line.GetSwitchValueASCII(switches::kInstallFromWebstore); 289 if (!Extension::IdIsValid(id)) { 290 LOG(ERROR) << "Invalid id for " << switches::kInstallFromWebstore 291 << " : '" << id << "'"; 292 return false; 293 } 294 295 AppInstallHelper helper; 296 helper.BeginInstall(profile, id, 297 !cmd_line.HasSwitch(switches::kForceAppMode), 298 base::MessageLoop::QuitWhenIdleClosure()); 299 300 base::MessageLoop::current()->Run(); 301 if (!helper.success()) 302 LOG(ERROR) << "InstallFromWebstore failed with error: " << helper.error(); 303 return helper.success(); 304 } 305 306 void StartupHelper::LimitedInstallFromWebstore( 307 const CommandLine& cmd_line, 308 Profile* profile, 309 base::Callback<void()> done_callback) { 310 std::string id = WebStoreIdFromLimitedInstallCmdLine(cmd_line); 311 if (!Extension::IdIsValid(id)) { 312 LOG(ERROR) << "Invalid index for " << switches::kLimitedInstallFromWebstore; 313 done_callback.Run(); 314 return; 315 } 316 317 AppInstallHelper* helper = new AppInstallHelper(); 318 helper->BeginInstall(profile, id, false /*show_prompt*/, 319 base::Bind(&DeleteHelperAndRunCallback, 320 helper, done_callback)); 321 } 322 323 std::string StartupHelper::WebStoreIdFromLimitedInstallCmdLine( 324 const CommandLine& cmd_line) { 325 std::string index = cmd_line.GetSwitchValueASCII( 326 switches::kLimitedInstallFromWebstore); 327 std::string id; 328 if (index == "1") { 329 id = "nckgahadagoaajjgafhacjanaoiihapd"; 330 } else if (index == "2") { 331 id = "ecglahbcnmdpdciemllbhojghbkagdje"; 332 } 333 return id; 334 } 335 336 StartupHelper::~StartupHelper() { 337 if (pack_job_.get()) 338 pack_job_->ClearClient(); 339 } 340 341 } // namespace extensions 342