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