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