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       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