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