installer_state.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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/installer/util/installer_state.h"
6
7#include <algorithm>
8#include <functional>
9#include <utility>
10
11#include "base/command_line.h"
12#include "base/file_util.h"
13#include "base/file_version_info.h"
14#include "base/files/file_enumerator.h"
15#include "base/logging.h"
16#include "base/memory/scoped_ptr.h"
17#include "base/strings/string_util.h"
18#include "base/strings/utf_string_conversions.h"
19#include "base/win/registry.h"
20#include "base/win/scoped_handle.h"
21#include "chrome/installer/util/delete_tree_work_item.h"
22#include "chrome/installer/util/helper.h"
23#include "chrome/installer/util/install_util.h"
24#include "chrome/installer/util/installation_state.h"
25#include "chrome/installer/util/master_preferences.h"
26#include "chrome/installer/util/master_preferences_constants.h"
27#include "chrome/installer/util/product.h"
28#include "chrome/installer/util/work_item.h"
29#include "chrome/installer/util/work_item_list.h"
30
31namespace installer {
32
33bool InstallerState::IsMultiInstallUpdate(const MasterPreferences& prefs,
34    const InstallationState& machine_state) {
35  // First, is the package present?
36  const ProductState* package =
37      machine_state.GetProductState(level_ == SYSTEM_LEVEL,
38                                    BrowserDistribution::CHROME_BINARIES);
39  if (package == NULL) {
40    // The multi-install package has not been installed, so it certainly isn't
41    // being updated.
42    return false;
43  }
44
45  BrowserDistribution::Type types[2];
46  size_t num_types = 0;
47  if (prefs.install_chrome())
48    types[num_types++] = BrowserDistribution::CHROME_BROWSER;
49  if (prefs.install_chrome_frame())
50    types[num_types++] = BrowserDistribution::CHROME_FRAME;
51
52  for (const BrowserDistribution::Type* scan = &types[0],
53           *end = &types[num_types]; scan != end; ++scan) {
54    const ProductState* product =
55        machine_state.GetProductState(level_ == SYSTEM_LEVEL, *scan);
56    if (product == NULL) {
57      VLOG(2) << "It seems that distribution type " << *scan
58              << " is being installed for the first time.";
59      return false;
60    }
61    if (!product->channel().Equals(package->channel())) {
62      VLOG(2) << "It seems that distribution type " << *scan
63              << " is being over installed.";
64      return false;
65    }
66  }
67
68  VLOG(2) << "It seems that the package is being updated.";
69
70  return true;
71}
72
73InstallerState::InstallerState()
74    : operation_(UNINITIALIZED),
75      state_type_(BrowserDistribution::CHROME_BROWSER),
76      multi_package_distribution_(NULL),
77      level_(UNKNOWN_LEVEL),
78      package_type_(UNKNOWN_PACKAGE_TYPE),
79      root_key_(NULL),
80      msi_(false),
81      verbose_logging_(false),
82      ensure_google_update_present_(false) {
83}
84
85InstallerState::InstallerState(Level level)
86    : operation_(UNINITIALIZED),
87      state_type_(BrowserDistribution::CHROME_BROWSER),
88      multi_package_distribution_(NULL),
89      level_(UNKNOWN_LEVEL),
90      package_type_(UNKNOWN_PACKAGE_TYPE),
91      root_key_(NULL),
92      msi_(false),
93      verbose_logging_(false),
94      ensure_google_update_present_(false) {
95  // Use set_level() so that root_key_ is updated properly.
96  set_level(level);
97}
98
99void InstallerState::Initialize(const CommandLine& command_line,
100                                const MasterPreferences& prefs,
101                                const InstallationState& machine_state) {
102  Clear();
103
104  bool pref_bool;
105  if (!prefs.GetBool(master_preferences::kSystemLevel, &pref_bool))
106    pref_bool = false;
107  set_level(pref_bool ? SYSTEM_LEVEL : USER_LEVEL);
108
109  if (!prefs.GetBool(master_preferences::kVerboseLogging, &verbose_logging_))
110    verbose_logging_ = false;
111
112  if (!prefs.GetBool(master_preferences::kMultiInstall, &pref_bool))
113    pref_bool = false;
114  set_package_type(pref_bool ? MULTI_PACKAGE : SINGLE_PACKAGE);
115
116  if (!prefs.GetBool(master_preferences::kMsi, &msi_))
117    msi_ = false;
118
119  ensure_google_update_present_ =
120      command_line.HasSwitch(installer::switches::kEnsureGoogleUpdatePresent);
121
122  const bool is_uninstall = command_line.HasSwitch(switches::kUninstall);
123
124  if (prefs.install_chrome()) {
125    Product* p = AddProductFromPreferences(
126        BrowserDistribution::CHROME_BROWSER, prefs, machine_state);
127    VLOG(1) << (is_uninstall ? "Uninstall" : "Install")
128            << " distribution: " << p->distribution()->GetDisplayName();
129  }
130  if (prefs.install_chrome_frame()) {
131    Product* p = AddProductFromPreferences(
132        BrowserDistribution::CHROME_FRAME, prefs, machine_state);
133    VLOG(1) << (is_uninstall ? "Uninstall" : "Install")
134            << " distribution: " << p->distribution()->GetDisplayName();
135  }
136
137  if (prefs.install_chrome_app_launcher()) {
138    Product* p = AddProductFromPreferences(
139        BrowserDistribution::CHROME_APP_HOST, prefs, machine_state);
140    VLOG(1) << (is_uninstall ? "Uninstall" : "Install")
141            << " distribution: " << p->distribution()->GetDisplayName();
142  }
143
144  if (!is_uninstall && is_multi_install()) {
145    bool need_binaries = false;
146    if (FindProduct(BrowserDistribution::CHROME_APP_HOST)) {
147      // App Host will happily use Chrome at system level, or binaries at system
148      // level, even if app host is user level.
149      const ProductState* chrome_state = machine_state.GetProductState(
150          true,  // system level
151          BrowserDistribution::CHROME_BROWSER);
152      // If Chrome is at system-level, multi- or otherwise. We'll use it.
153      if (!chrome_state) {
154        const ProductState* binaries_state = machine_state.GetProductState(
155            true,  // system level
156            BrowserDistribution::CHROME_BINARIES);
157        if (!binaries_state)
158          need_binaries = true;
159      }
160    }
161
162    // Chrome/Chrome Frame multi need Binaries at their own level.
163    if (FindProduct(BrowserDistribution::CHROME_BROWSER))
164      need_binaries = true;
165
166    if (FindProduct(BrowserDistribution::CHROME_FRAME))
167      need_binaries = true;
168
169    if (need_binaries && !FindProduct(BrowserDistribution::CHROME_BINARIES)) {
170      // Force binaries to be installed/updated.
171      Product* p = AddProductFromPreferences(
172          BrowserDistribution::CHROME_BINARIES, prefs, machine_state);
173      VLOG(1) << "Install distribution: "
174              << p->distribution()->GetDisplayName();
175    }
176  }
177
178  if (is_uninstall && prefs.is_multi_install()) {
179    if (FindProduct(BrowserDistribution::CHROME_BROWSER)) {
180      // Uninstall each product of type |type| listed below based on the
181      // presence or absence of |switch_name| in that product's uninstall
182      // command.
183      const struct {
184        BrowserDistribution::Type type;
185        const char* switch_name;
186        bool switch_expected;
187      } conditional_additions[] = {
188        // If Chrome Frame is installed in Ready Mode, remove it with Chrome.
189        { BrowserDistribution::CHROME_FRAME,
190          switches::kChromeFrameReadyMode,
191          true },
192        // If the App Host is installed, but not the App Launcher, remove it
193        // with Chrome. Note however that for system-level Chrome uninstalls,
194        // any installed user-level App Host will remain even if there is no
195        // App Launcher present (the orphaned app_host.exe will prompt the user
196        // for further action when executed).
197        { BrowserDistribution::CHROME_APP_HOST,
198          switches::kChromeAppLauncher,
199          false },
200      };
201
202      for (size_t i = 0; i < arraysize(conditional_additions); ++i) {
203        const ProductState* product_state = machine_state.GetProductState(
204            system_install(), conditional_additions[i].type);
205        if (product_state != NULL &&
206            product_state->uninstall_command().HasSwitch(
207                conditional_additions[i].switch_name) ==
208                    conditional_additions[i].switch_expected &&
209            !FindProduct(conditional_additions[i].type)) {
210          Product* p = AddProductFromPreferences(
211              conditional_additions[i].type, prefs, machine_state);
212          VLOG(1) << "Uninstall distribution: "
213                  << p->distribution()->GetDisplayName();
214        }
215      }
216    }
217
218    bool keep_binaries = false;
219    // Look for a multi-install product that is not the binaries and that is not
220    // being uninstalled. If not found, binaries are uninstalled too.
221    for (size_t i = 0; i < BrowserDistribution::NUM_TYPES; ++i) {
222      BrowserDistribution::Type type =
223          static_cast<BrowserDistribution::Type>(i);
224
225      if (type == BrowserDistribution::CHROME_BINARIES)
226        continue;
227
228      const ProductState* product_state =
229          machine_state.GetProductState(system_install(), type);
230      if (product_state == NULL) {
231        // The product is not installed.
232        continue;
233      }
234
235      if (!product_state->is_multi_install() &&
236          type != BrowserDistribution::CHROME_BROWSER) {
237        // The product is not sharing the binaries. It is ordinarily impossible
238        // for single-install Chrome to be installed along with any
239        // multi-install product. Treat single-install Chrome the same as any
240        // multi-install product just in case the impossible happens.
241        continue;
242      }
243
244      // The product is installed.
245
246      if (!FindProduct(type)) {
247        // The product is not being uninstalled.
248        if (type != BrowserDistribution::CHROME_APP_HOST) {
249          keep_binaries = true;
250          break;
251        } else {
252          // If binaries/chrome are at system-level, we can discard them at
253          // user-level...
254          if (!machine_state.GetProductState(
255                  true,  // system-level
256                  BrowserDistribution::CHROME_BROWSER) &&
257              !machine_state.GetProductState(
258                  true,  // system-level
259                  BrowserDistribution::CHROME_BINARIES)) {
260            // ... otherwise keep them.
261            keep_binaries = true;
262            break;
263          }
264
265        }
266      }
267
268      // The product is being uninstalled.
269    }
270    if (!keep_binaries &&
271        machine_state.GetProductState(system_install(),
272                                      BrowserDistribution::CHROME_BINARIES)) {
273      Product* p = AddProductFromPreferences(
274          BrowserDistribution::CHROME_BINARIES, prefs, machine_state);
275      VLOG(1) << (is_uninstall ? "Uninstall" : "Install")
276              << " distribution: " << p->distribution()->GetDisplayName();
277    }
278  }
279
280  BrowserDistribution* operand = NULL;
281
282  if (is_uninstall) {
283    operation_ = UNINSTALL;
284  } else if (!prefs.is_multi_install()) {
285    // For a single-install, the current browser dist is the operand.
286    operand = BrowserDistribution::GetDistribution();
287    operation_ = SINGLE_INSTALL_OR_UPDATE;
288  } else if (IsMultiInstallUpdate(prefs, machine_state)) {
289    // Updates driven by Google Update take place under the multi-installer's
290    // app guid.
291    operand = multi_package_distribution_;
292    operation_ = MULTI_UPDATE;
293  } else {
294    operation_ = MULTI_INSTALL;
295  }
296
297  // Initial, over, and un-installs will take place under one of the
298  // product app guids (Chrome, Chrome Frame, App Host, or Binaries, in order of
299  // preference).
300  if (operand == NULL) {
301    BrowserDistribution::Type operand_distribution_type =
302        BrowserDistribution::CHROME_BINARIES;
303    if (prefs.install_chrome())
304      operand_distribution_type = BrowserDistribution::CHROME_BROWSER;
305    else if (prefs.install_chrome_frame())
306      operand_distribution_type = BrowserDistribution::CHROME_FRAME;
307    else if (prefs.install_chrome_app_launcher())
308      operand_distribution_type = BrowserDistribution::CHROME_APP_HOST;
309
310    operand = BrowserDistribution::GetSpecificDistribution(
311        operand_distribution_type);
312  }
313
314  state_key_ = operand->GetStateKey();
315  state_type_ = operand->GetType();
316
317  // Parse --critical-update-version=W.X.Y.Z
318  std::string critical_version_value(
319      command_line.GetSwitchValueASCII(switches::kCriticalUpdateVersion));
320  critical_update_version_ = Version(critical_version_value);
321}
322
323void InstallerState::set_level(Level level) {
324  level_ = level;
325  switch (level) {
326    case USER_LEVEL:
327      root_key_ = HKEY_CURRENT_USER;
328      break;
329    case SYSTEM_LEVEL:
330      root_key_ = HKEY_LOCAL_MACHINE;
331      break;
332    default:
333      DCHECK(level == UNKNOWN_LEVEL);
334      level_ = UNKNOWN_LEVEL;
335      root_key_ = NULL;
336      break;
337  }
338}
339
340void InstallerState::set_package_type(PackageType type) {
341  package_type_ = type;
342  switch (type) {
343    case SINGLE_PACKAGE:
344      multi_package_distribution_ = NULL;
345      break;
346    case MULTI_PACKAGE:
347      multi_package_distribution_ =
348          BrowserDistribution::GetSpecificDistribution(
349              BrowserDistribution::CHROME_BINARIES);
350      break;
351    default:
352      DCHECK(type == UNKNOWN_PACKAGE_TYPE);
353      package_type_ = UNKNOWN_PACKAGE_TYPE;
354      multi_package_distribution_ = NULL;
355      break;
356  }
357}
358
359// Returns the Chrome binaries directory for multi-install or |dist|'s directory
360// otherwise.
361base::FilePath InstallerState::GetDefaultProductInstallPath(
362    BrowserDistribution* dist) const {
363  DCHECK(dist);
364  DCHECK(package_type_ != UNKNOWN_PACKAGE_TYPE);
365
366  if (package_type_ == SINGLE_PACKAGE) {
367    return GetChromeInstallPath(system_install(), dist);
368  } else {
369    return GetChromeInstallPath(system_install(),
370        BrowserDistribution::GetSpecificDistribution(
371            BrowserDistribution::CHROME_BINARIES));
372  }
373}
374
375// Evaluates a product's eligibility for participation in this operation.
376// We never expect these checks to fail, hence they all terminate the process in
377// debug builds.  See the log messages for details.
378bool InstallerState::CanAddProduct(const Product& product,
379                                   const base::FilePath* product_dir) const {
380  switch (package_type_) {
381    case SINGLE_PACKAGE:
382      if (!products_.empty()) {
383        LOG(DFATAL) << "Cannot process more than one single-install product.";
384        return false;
385      }
386      break;
387    case MULTI_PACKAGE:
388      if (!product.HasOption(kOptionMultiInstall)) {
389        LOG(DFATAL) << "Cannot process a single-install product with a "
390                       "multi-install state.";
391        return false;
392      }
393      if (FindProduct(product.distribution()->GetType()) != NULL) {
394        LOG(DFATAL) << "Cannot process more than one product of the same type.";
395        return false;
396      }
397      if (!target_path_.empty()) {
398        base::FilePath default_dir;
399        if (product_dir == NULL)
400          default_dir = GetDefaultProductInstallPath(product.distribution());
401        if (!base::FilePath::CompareEqualIgnoreCase(
402                (product_dir == NULL ? default_dir : *product_dir).value(),
403                target_path_.value())) {
404          LOG(DFATAL) << "Cannot process products in different directories.";
405          return false;
406        }
407      }
408      break;
409    default:
410      DCHECK_EQ(UNKNOWN_PACKAGE_TYPE, package_type_);
411      break;
412  }
413  return true;
414}
415
416// Adds |product|, installed in |product_dir| to this object's collection.  If
417// |product_dir| is NULL, the product's default install location is used.
418// Returns NULL if |product| is incompatible with this object.  Otherwise,
419// returns a pointer to the product (ownership is held by this object).
420Product* InstallerState::AddProductInDirectory(
421    const base::FilePath* product_dir,
422    scoped_ptr<Product>* product) {
423  DCHECK(product != NULL);
424  DCHECK(product->get() != NULL);
425  const Product& the_product = *product->get();
426
427  if (!CanAddProduct(the_product, product_dir))
428    return NULL;
429
430  if (package_type_ == UNKNOWN_PACKAGE_TYPE) {
431    set_package_type(the_product.HasOption(kOptionMultiInstall) ?
432                         MULTI_PACKAGE : SINGLE_PACKAGE);
433  }
434
435  if (target_path_.empty()) {
436    if (product_dir == NULL)
437      target_path_ = GetDefaultProductInstallPath(the_product.distribution());
438    else
439      target_path_ = *product_dir;
440  }
441
442  if (state_key_.empty())
443    state_key_ = the_product.distribution()->GetStateKey();
444
445  products_.push_back(product->release());
446  return products_[products_.size() - 1];
447}
448
449Product* InstallerState::AddProduct(scoped_ptr<Product>* product) {
450  return AddProductInDirectory(NULL, product);
451}
452
453// Adds a product of type |distribution_type| constructed on the basis of
454// |prefs|, setting this object's msi flag if the product is represented in
455// |machine_state| and is msi-installed.  Returns the product that was added,
456// or NULL if |state| is incompatible with this object.  Ownership is not passed
457// to the caller.
458Product* InstallerState::AddProductFromPreferences(
459    BrowserDistribution::Type distribution_type,
460    const MasterPreferences& prefs,
461    const InstallationState& machine_state) {
462  scoped_ptr<Product> product_ptr(
463      new Product(BrowserDistribution::GetSpecificDistribution(
464          distribution_type)));
465  product_ptr->InitializeFromPreferences(prefs);
466
467  Product* product = AddProductInDirectory(NULL, &product_ptr);
468
469  if (product != NULL && !msi_) {
470    const ProductState* product_state = machine_state.GetProductState(
471        system_install(), distribution_type);
472    if (product_state != NULL)
473      msi_ = product_state->is_msi();
474  }
475
476  return product;
477}
478
479Product* InstallerState::AddProductFromState(
480    BrowserDistribution::Type type,
481    const ProductState& state) {
482  scoped_ptr<Product> product_ptr(
483      new Product(BrowserDistribution::GetSpecificDistribution(type)));
484  product_ptr->InitializeFromUninstallCommand(state.uninstall_command());
485
486  // Strip off <version>/Installer/setup.exe; see GetInstallerDirectory().
487  base::FilePath product_dir =
488      state.GetSetupPath().DirName().DirName().DirName();
489
490  Product* product = AddProductInDirectory(&product_dir, &product_ptr);
491
492  if (product != NULL)
493    msi_ |= state.is_msi();
494
495  return product;
496}
497
498bool InstallerState::system_install() const {
499  DCHECK(level_ == USER_LEVEL || level_ == SYSTEM_LEVEL);
500  return level_ == SYSTEM_LEVEL;
501}
502
503bool InstallerState::is_multi_install() const {
504  DCHECK(package_type_ == SINGLE_PACKAGE || package_type_ == MULTI_PACKAGE);
505  return package_type_ != SINGLE_PACKAGE;
506}
507
508bool InstallerState::RemoveProduct(const Product* product) {
509  ScopedVector<Product>::iterator it =
510      std::find(products_.begin(), products_.end(), product);
511  if (it != products_.end()) {
512    products_.weak_erase(it);
513    return true;
514  }
515  return false;
516}
517
518const Product* InstallerState::FindProduct(
519    BrowserDistribution::Type distribution_type) const {
520  for (Products::const_iterator scan = products_.begin(), end = products_.end();
521       scan != end; ++scan) {
522     if ((*scan)->is_type(distribution_type))
523       return *scan;
524  }
525  return NULL;
526}
527
528Version* InstallerState::GetCurrentVersion(
529    const InstallationState& machine_state) const {
530  DCHECK(!products_.empty());
531  scoped_ptr<Version> current_version;
532  // If we're doing a multi-install, the current version may be either an
533  // existing multi or an existing single product that is being migrated
534  // in place (i.e., Chrome).  In the latter case, there is no existing
535  // CHROME_BINARIES installation so we need to search for the product.
536  BrowserDistribution::Type prod_type;
537  if (package_type_ == MULTI_PACKAGE) {
538    prod_type = BrowserDistribution::CHROME_BINARIES;
539    if (machine_state.GetProductState(level_ == SYSTEM_LEVEL,
540                                      prod_type) == NULL) {
541      // Search for a product on which we're operating that is installed in our
542      // target directory.
543      Products::const_iterator end = products().end();
544      for (Products::const_iterator scan = products().begin(); scan != end;
545           ++scan) {
546        BrowserDistribution::Type product_type =
547            (*scan)->distribution()->GetType();
548        const ProductState* state =
549            machine_state.GetProductState(level_ == SYSTEM_LEVEL, product_type);
550        if (state != NULL && target_path_.IsParent(state->GetSetupPath())) {
551          prod_type = product_type;
552          break;
553        }
554      }
555    }
556  } else {
557    prod_type = products_[0]->distribution()->GetType();
558  }
559  const ProductState* product_state =
560      machine_state.GetProductState(level_ == SYSTEM_LEVEL, prod_type);
561
562  if (product_state != NULL) {
563    const Version* version = NULL;
564
565    // Be aware that there might be a pending "new_chrome.exe" already in the
566    // installation path.  If so, we use old_version, which holds the version of
567    // "chrome.exe" itself.
568    if (base::PathExists(target_path().Append(kChromeNewExe)))
569      version = product_state->old_version();
570
571    if (version == NULL)
572      version = &product_state->version();
573
574    current_version.reset(new Version(*version));
575  }
576
577  return current_version.release();
578}
579
580Version InstallerState::DetermineCriticalVersion(
581    const Version* current_version,
582    const Version& new_version) const {
583  DCHECK(current_version == NULL || current_version->IsValid());
584  DCHECK(new_version.IsValid());
585  if (critical_update_version_.IsValid() &&
586      (current_version == NULL ||
587       (current_version->CompareTo(critical_update_version_) < 0)) &&
588      new_version.CompareTo(critical_update_version_) >= 0) {
589    return critical_update_version_;
590  }
591  return Version();
592}
593
594bool InstallerState::IsChromeFrameRunning(
595    const InstallationState& machine_state) const {
596  return AnyExistsAndIsInUse(machine_state, CHROME_FRAME_DLL);
597}
598
599bool InstallerState::AreBinariesInUse(
600    const InstallationState& machine_state) const {
601  return AnyExistsAndIsInUse(
602      machine_state,
603      (CHROME_FRAME_HELPER_EXE | CHROME_FRAME_HELPER_DLL |
604       CHROME_FRAME_DLL | CHROME_DLL));
605}
606
607base::FilePath InstallerState::GetInstallerDirectory(
608    const Version& version) const {
609  return target_path().Append(ASCIIToWide(version.GetString()))
610      .Append(kInstallerDir);
611}
612
613// static
614bool InstallerState::IsFileInUse(const base::FilePath& file) {
615  // Call CreateFile with a share mode of 0 which should cause this to fail
616  // with ERROR_SHARING_VIOLATION if the file exists and is in-use.
617  return !base::win::ScopedHandle(CreateFile(file.value().c_str(),
618                                             GENERIC_WRITE, 0, NULL,
619                                             OPEN_EXISTING, 0, 0)).IsValid();
620}
621
622void InstallerState::Clear() {
623  operation_ = UNINITIALIZED;
624  target_path_.clear();
625  state_key_.clear();
626  state_type_ = BrowserDistribution::CHROME_BROWSER;
627  products_.clear();
628  multi_package_distribution_ = NULL;
629  critical_update_version_ = base::Version();
630  level_ = UNKNOWN_LEVEL;
631  package_type_ = UNKNOWN_PACKAGE_TYPE;
632  root_key_ = NULL;
633  msi_ = false;
634  verbose_logging_ = false;
635  ensure_google_update_present_ = false;
636}
637
638bool InstallerState::AnyExistsAndIsInUse(
639    const InstallationState& machine_state,
640    uint32 file_bits) const {
641  static const wchar_t* const kBinaryFileNames[] = {
642    kChromeDll,
643    kChromeFrameDll,
644    kChromeFrameHelperDll,
645    kChromeFrameHelperExe,
646  };
647  DCHECK_NE(file_bits, 0U);
648  DCHECK_LT(file_bits, 1U << NUM_BINARIES);
649  COMPILE_ASSERT(CHROME_DLL == 1, no_youre_out_of_order);
650  COMPILE_ASSERT(CHROME_FRAME_DLL == 2, no_youre_out_of_order);
651  COMPILE_ASSERT(CHROME_FRAME_HELPER_DLL == 4, no_youre_out_of_order);
652  COMPILE_ASSERT(CHROME_FRAME_HELPER_EXE == 8, no_youre_out_of_order);
653
654  // Check only for the current version (i.e., the version we are upgrading
655  // _from_). Later versions from pending in-use updates need not be checked
656  // since the current version is guaranteed to be in use if any such are.
657  bool in_use = false;
658  scoped_ptr<Version> current_version(GetCurrentVersion(machine_state));
659  if (!current_version)
660    return false;
661  base::FilePath directory(
662      target_path().AppendASCII(current_version->GetString()));
663  for (int i = 0; i < NUM_BINARIES; ++i) {
664    if (!(file_bits & (1U << i)))
665      continue;
666    base::FilePath file(directory.Append(kBinaryFileNames[i]));
667    if (base::PathExists(file) && IsFileInUse(file))
668      return true;
669  }
670  return false;
671}
672
673void InstallerState::GetExistingExeVersions(
674    std::set<std::string>* existing_versions) const {
675
676  static const wchar_t* const kChromeFilenames[] = {
677    installer::kChromeExe,
678    installer::kChromeNewExe,
679    installer::kChromeOldExe,
680  };
681
682  for (int i = 0; i < arraysize(kChromeFilenames); ++i) {
683    base::FilePath chrome_exe(target_path().Append(kChromeFilenames[i]));
684    scoped_ptr<FileVersionInfo> file_version_info(
685        FileVersionInfo::CreateFileVersionInfo(chrome_exe));
686    if (file_version_info) {
687      string16 version_string = file_version_info->file_version();
688      if (!version_string.empty() && IsStringASCII(version_string))
689        existing_versions->insert(WideToASCII(version_string));
690    }
691  }
692}
693
694void InstallerState::RemoveOldVersionDirectories(
695    const Version& new_version,
696    Version* existing_version,
697    const base::FilePath& temp_path) const {
698  Version version;
699  scoped_ptr<WorkItem> item;
700
701  std::set<std::string> existing_version_strings;
702  existing_version_strings.insert(new_version.GetString());
703  if (existing_version)
704    existing_version_strings.insert(existing_version->GetString());
705
706  // Make sure not to delete any version dir that is "referenced" by an existing
707  // Chrome executable.
708  GetExistingExeVersions(&existing_version_strings);
709
710  // Try to delete all directories that are not in the set we care to keep.
711  base::FileEnumerator version_enum(target_path(), false,
712                                    base::FileEnumerator::DIRECTORIES);
713  for (base::FilePath next_version = version_enum.Next(); !next_version.empty();
714       next_version = version_enum.Next()) {
715    base::FilePath dir_name(next_version.BaseName());
716    version = Version(WideToASCII(dir_name.value()));
717    // Delete the version folder if it is less than the new version and not
718    // equal to the old version (if we have an old version).
719    if (version.IsValid() &&
720        existing_version_strings.count(version.GetString()) == 0) {
721      // Note: temporarily log old version deletion at ERROR level to make it
722      // more likely we see this in the installer log.
723      LOG(ERROR) << "Deleting old version directory: " << next_version.value();
724
725      // Attempt to recursively delete the old version dir.
726      bool delete_succeeded = base::DeleteFile(next_version, true);
727
728      // Note: temporarily log old version deletion at ERROR level to make it
729      // more likely we see this in the installer log.
730      LOG_IF(ERROR, !delete_succeeded)
731          << "Failed to delete old version directory: " << next_version.value();
732    }
733  }
734}
735
736void InstallerState::AddComDllList(
737    std::vector<base::FilePath>* com_dll_list) const {
738  std::for_each(products_.begin(), products_.end(),
739                std::bind2nd(std::mem_fun(&Product::AddComDllList),
740                             com_dll_list));
741}
742
743bool InstallerState::SetChannelFlags(bool set,
744                                     ChannelInfo* channel_info) const {
745  bool modified = false;
746  for (Products::const_iterator scan = products_.begin(), end = products_.end();
747       scan != end; ++scan) {
748     modified |= (*scan)->SetChannelFlags(set, channel_info);
749  }
750  return modified;
751}
752
753void InstallerState::UpdateStage(installer::InstallerStage stage) const {
754  InstallUtil::UpdateInstallerStage(system_install(), state_key_, stage);
755}
756
757void InstallerState::UpdateChannels() const {
758  if (operation_ != MULTI_INSTALL && operation_ != MULTI_UPDATE) {
759    VLOG(1) << "InstallerState::UpdateChannels noop: " << operation_;
760    return;
761  }
762
763  // Update the "ap" value for the product being installed/updated.  We get the
764  // current value from the registry since the InstallationState instance used
765  // by the bulk of the installer does not track changes made by UpdateStage.
766  // Create the app's ClientState key if it doesn't exist.
767  ChannelInfo channel_info;
768  base::win::RegKey state_key;
769  LONG result = state_key.Create(root_key_, state_key_.c_str(),
770                                 KEY_QUERY_VALUE | KEY_SET_VALUE);
771  if (result == ERROR_SUCCESS) {
772    channel_info.Initialize(state_key);
773
774    // This is a multi-install product.
775    bool modified = channel_info.SetMultiInstall(true);
776
777    // Add the appropriate modifiers for all products and their options.
778    modified |= SetChannelFlags(true, &channel_info);
779
780    VLOG(1) << "ap: " << channel_info.value();
781
782    // Write the results if needed.
783    if (modified)
784      channel_info.Write(&state_key);
785
786    // Remove the -stage: modifier since we don't want to propagate that to the
787    // other app_guids.
788    channel_info.SetStage(NULL);
789
790    // Synchronize the other products and the package with this one.
791    ChannelInfo other_info;
792    for (int i = 0; i < BrowserDistribution::NUM_TYPES; ++i) {
793      BrowserDistribution::Type type =
794          static_cast<BrowserDistribution::Type>(i);
795      // Skip the app_guid we started with.
796      if (type == state_type_)
797        continue;
798      BrowserDistribution* dist = NULL;
799      // Always operate on the binaries.
800      if (i == BrowserDistribution::CHROME_BINARIES) {
801        dist = multi_package_distribution_;
802      } else {
803        const Product* product = FindProduct(type);
804        // Skip this one if it's for a product we're not operating on.
805        if (product == NULL)
806          continue;
807        dist = product->distribution();
808      }
809      result = state_key.Create(root_key_, dist->GetStateKey().c_str(),
810                                KEY_QUERY_VALUE | KEY_SET_VALUE);
811      if (result == ERROR_SUCCESS) {
812        other_info.Initialize(state_key);
813        if (!other_info.Equals(channel_info))
814          channel_info.Write(&state_key);
815      } else {
816        LOG(ERROR) << "Failed opening key " << dist->GetStateKey()
817                   << " to update app channels; result: " << result;
818      }
819    }
820  } else {
821    LOG(ERROR) << "Failed opening key " << state_key_
822               << " to update app channels; result: " << result;
823  }
824}
825
826void InstallerState::WriteInstallerResult(
827    InstallStatus status,
828    int string_resource_id,
829    const std::wstring* const launch_cmd) const {
830  // Use a no-rollback list since this is a best-effort deal.
831  scoped_ptr<WorkItemList> install_list(
832      WorkItem::CreateNoRollbackWorkItemList());
833  const bool system_install = this->system_install();
834  // Write the value for all products upon which we're operating.
835  Products::const_iterator end = products().end();
836  for (Products::const_iterator scan = products().begin(); scan != end;
837       ++scan) {
838    InstallUtil::AddInstallerResultItems(
839        system_install, (*scan)->distribution()->GetStateKey(), status,
840        string_resource_id, launch_cmd, install_list.get());
841  }
842  // And for the binaries if this is a multi-install.
843  if (is_multi_install()) {
844    InstallUtil::AddInstallerResultItems(
845        system_install, multi_package_binaries_distribution()->GetStateKey(),
846        status, string_resource_id, launch_cmd, install_list.get());
847  }
848  if (!install_list->Do())
849    LOG(ERROR) << "Failed to record installer error information in registry.";
850}
851
852bool InstallerState::RequiresActiveSetup() const {
853  return system_install() && FindProduct(BrowserDistribution::CHROME_BROWSER);
854}
855
856}  // namespace installer
857