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