// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/installer/util/channel_info.h" #include "base/logging.h" #include "base/win/registry.h" #include "chrome/installer/util/google_update_constants.h" #include "chrome/installer/util/util_constants.h" using base::win::RegKey; namespace { const wchar_t kModChrome[] = L"-chrome"; const wchar_t kModChromeFrame[] = L"-chromeframe"; // TODO(huangs): Remove by M27. const wchar_t kModAppHostDeprecated[] = L"-apphost"; const wchar_t kModAppLauncher[] = L"-applauncher"; const wchar_t kModMultiInstall[] = L"-multi"; const wchar_t kModReadyMode[] = L"-readymode"; const wchar_t kModStage[] = L"-stage:"; const wchar_t kSfxFull[] = L"-full"; const wchar_t kSfxMigrating[] = L"-migrating"; const wchar_t kSfxMultiFail[] = L"-multifail"; const wchar_t* const kChannels[] = { installer::kChromeChannelBeta, installer::kChromeChannelDev, installer::kChromeChannelStableExplicit }; const wchar_t* const kModifiers[] = { kModStage, kModMultiInstall, kModChrome, kModChromeFrame, kModAppHostDeprecated, // TODO(huangs): Remove by M27. kModAppLauncher, kModReadyMode, kSfxMultiFail, kSfxMigrating, kSfxFull, }; enum ModifierIndex { MOD_STAGE, MOD_MULTI_INSTALL, MOD_CHROME, MOD_CHROME_FRAME, MOD_APP_HOST_DEPRECATED, // TODO(huangs): Remove by M27. MOD_APP_LAUNCHER, MOD_READY_MODE, SFX_MULTI_FAIL, SFX_MIGRATING, SFX_FULL, NUM_MODIFIERS }; COMPILE_ASSERT(NUM_MODIFIERS == arraysize(kModifiers), kModifiers_disagrees_with_ModifierIndex_comma_they_must_match_bang); // Returns true if the modifier is found, in which case |position| holds the // location at which the modifier was found. The number of characters in the // modifier is returned in |length|, if non-NULL. bool FindModifier(ModifierIndex index, const std::wstring& ap_value, std::wstring::size_type* position, std::wstring::size_type* length) { DCHECK(position != NULL); std::wstring::size_type mod_position = std::wstring::npos; std::wstring::size_type mod_length = std::wstring::traits_type::length(kModifiers[index]); const bool mod_takes_arg = (kModifiers[index][mod_length - 1] == L':'); std::wstring::size_type pos = 0; do { mod_position = ap_value.find(kModifiers[index], pos, mod_length); if (mod_position == std::wstring::npos) return false; // Modifier not found. pos = mod_position + mod_length; // Modifiers that take an argument gobble up to the next separator or to the // end. if (mod_takes_arg) { pos = ap_value.find(L'-', pos); if (pos == std::wstring::npos) pos = ap_value.size(); break; } // Regular modifiers must be followed by '-' or the end of the string. } while (pos != ap_value.size() && ap_value[pos] != L'-'); DCHECK_NE(mod_position, std::wstring::npos); *position = mod_position; if (length != NULL) *length = pos - mod_position; return true; } bool HasModifier(ModifierIndex index, const std::wstring& ap_value) { DCHECK(index >= 0 && index < NUM_MODIFIERS); std::wstring::size_type position; return FindModifier(index, ap_value, &position, NULL); } std::wstring::size_type FindInsertionPoint(ModifierIndex index, const std::wstring& ap_value) { // Return the location of the next modifier. std::wstring::size_type result; for (int scan = index + 1; scan < NUM_MODIFIERS; ++scan) { if (FindModifier(static_cast<ModifierIndex>(scan), ap_value, &result, NULL)) return result; } return ap_value.size(); } // Returns true if |ap_value| is modified. bool SetModifier(ModifierIndex index, bool set, std::wstring* ap_value) { DCHECK(index >= 0 && index < NUM_MODIFIERS); DCHECK(ap_value); std::wstring::size_type position; std::wstring::size_type length; bool have_modifier = FindModifier(index, *ap_value, &position, &length); if (set) { if (!have_modifier) { ap_value->insert(FindInsertionPoint(index, *ap_value), kModifiers[index]); return true; } } else { if (have_modifier) { ap_value->erase(position, length); return true; } } return false; } } // namespace namespace installer { bool ChannelInfo::Initialize(const RegKey& key) { LONG result = key.ReadValue(google_update::kRegApField, &value_); return result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND || result == ERROR_INVALID_HANDLE; } bool ChannelInfo::Write(RegKey* key) const { DCHECK(key); // Google Update deletes the value when it is empty, so we may as well, too. LONG result = value_.empty() ? key->DeleteValue(google_update::kRegApField) : key->WriteValue(google_update::kRegApField, value_.c_str()); if (result != ERROR_SUCCESS) { LOG(ERROR) << "Failed writing channel info; result: " << result; return false; } return true; } bool ChannelInfo::GetChannelName(std::wstring* channel_name) const { DCHECK(channel_name); if (value_.empty()) { channel_name->erase(); return true; } else { for (const wchar_t* const* scan = &kChannels[0], *const* end = &kChannels[arraysize(kChannels)]; scan != end; ++scan) { if (value_.find(*scan) != std::wstring::npos) { // Report channels with "stable" in them as stable (empty string). if (*scan == installer::kChromeChannelStableExplicit) channel_name->erase(); else channel_name->assign(*scan); return true; } } // There may be modifiers present. Strip them off and see if we're left // with the empty string (stable channel). std::wstring tmp_value = value_; for (int i = 0; i != NUM_MODIFIERS; ++i) { SetModifier(static_cast<ModifierIndex>(i), false, &tmp_value); } if (tmp_value.empty()) { channel_name->erase(); return true; } } return false; } bool ChannelInfo::IsChrome() const { return HasModifier(MOD_CHROME, value_); } bool ChannelInfo::SetChrome(bool value) { return SetModifier(MOD_CHROME, value, &value_); } bool ChannelInfo::IsChromeFrame() const { return HasModifier(MOD_CHROME_FRAME, value_); } bool ChannelInfo::SetChromeFrame(bool value) { return SetModifier(MOD_CHROME_FRAME, value, &value_); } bool ChannelInfo::IsAppLauncher() const { return HasModifier(MOD_APP_LAUNCHER, value_); } bool ChannelInfo::SetAppLauncher(bool value) { // Unconditionally remove -apphost since it has been deprecated. bool changed_app_host = SetModifier(MOD_APP_HOST_DEPRECATED, false, &value_); bool changed_app_launcher = SetModifier(MOD_APP_LAUNCHER, value, &value_); return changed_app_host || changed_app_launcher; } bool ChannelInfo::IsMultiInstall() const { return HasModifier(MOD_MULTI_INSTALL, value_); } bool ChannelInfo::SetMultiInstall(bool value) { return SetModifier(MOD_MULTI_INSTALL, value, &value_); } bool ChannelInfo::IsReadyMode() const { return HasModifier(MOD_READY_MODE, value_); } bool ChannelInfo::SetReadyMode(bool value) { return SetModifier(MOD_READY_MODE, value, &value_); } bool ChannelInfo::SetStage(const wchar_t* stage) { std::wstring::size_type position; std::wstring::size_type length; bool have_modifier = FindModifier(MOD_STAGE, value_, &position, &length); if (stage != NULL && *stage != L'\0') { std::wstring stage_str(kModStage); stage_str.append(stage); if (!have_modifier) { value_.insert(FindInsertionPoint(MOD_STAGE, value_), stage_str); return true; } if (value_.compare(position, length, stage_str) != 0) { value_.replace(position, length, stage_str); return true; } } else { if (have_modifier) { value_.erase(position, length); return true; } } return false; } std::wstring ChannelInfo::GetStage() const { std::wstring::size_type position; std::wstring::size_type length; if (FindModifier(MOD_STAGE, value_, &position, &length)) { // Return the portion after the prefix. std::wstring::size_type pfx_length = std::wstring::traits_type::length(kModStage); DCHECK_LE(pfx_length, length); return value_.substr(position + pfx_length, length - pfx_length); } return std::wstring(); } bool ChannelInfo::HasFullSuffix() const { return HasModifier(SFX_FULL, value_); } bool ChannelInfo::SetFullSuffix(bool value) { return SetModifier(SFX_FULL, value, &value_); } bool ChannelInfo::HasMultiFailSuffix() const { return HasModifier(SFX_MULTI_FAIL, value_); } bool ChannelInfo::SetMultiFailSuffix(bool value) { return SetModifier(SFX_MULTI_FAIL, value, &value_); } bool ChannelInfo::SetMigratingSuffix(bool value) { return SetModifier(SFX_MIGRATING, value, &value_); } bool ChannelInfo::HasMigratingSuffix() const { return HasModifier(SFX_MIGRATING, value_); } bool ChannelInfo::RemoveAllModifiersAndSuffixes() { bool modified = false; for (int scan = 0; scan < NUM_MODIFIERS; ++scan) { ModifierIndex index = static_cast<ModifierIndex>(scan); modified = SetModifier(index, false, &value_) || modified; } return modified; } } // namespace installer