Home | History | Annotate | Download | only in extensions
      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