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 base::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 = 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(); 207 bool success() { return success_; } 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 227 AppInstallHelper::AppInstallHelper() : success_(false) {} 228 229 AppInstallHelper::~AppInstallHelper() {} 230 231 WebstoreStandaloneInstaller::Callback AppInstallHelper::Callback() { 232 return base::Bind(&AppInstallHelper::OnAppInstallComplete, 233 base::Unretained(this)); 234 } 235 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 254 void AppInstallHelper::OnAppInstallComplete(bool success, 255 const std::string& error) { 256 success_ = success; 257 error_= error; 258 done_callback_.Run(); 259 } 260 261 void DeleteHelperAndRunCallback(AppInstallHelper* helper, 262 base::Callback<void()> callback) { 263 delete helper; 264 callback.Run(); 265 } 266 267 } // namespace 268 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 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 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 318 StartupHelper::~StartupHelper() { 319 if (pack_job_.get()) 320 pack_job_->ClearClient(); 321 } 322 323 } // namespace extensions 324