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/channel_info.h" 6 7#include "base/logging.h" 8#include "base/win/registry.h" 9#include "chrome/installer/util/google_update_constants.h" 10#include "chrome/installer/util/util_constants.h" 11 12using base::win::RegKey; 13 14namespace { 15 16const wchar_t kModChrome[] = L"-chrome"; 17const wchar_t kModChromeFrame[] = L"-chromeframe"; 18// TODO(huangs): Remove by M27. 19const wchar_t kModAppHostDeprecated[] = L"-apphost"; 20const wchar_t kModAppLauncher[] = L"-applauncher"; 21const wchar_t kModMultiInstall[] = L"-multi"; 22const wchar_t kModReadyMode[] = L"-readymode"; 23const wchar_t kModStage[] = L"-stage:"; 24const wchar_t kSfxFull[] = L"-full"; 25const wchar_t kSfxMultiFail[] = L"-multifail"; 26 27const wchar_t* const kChannels[] = { 28 installer::kChromeChannelBeta, 29 installer::kChromeChannelDev 30}; 31 32const wchar_t* const kModifiers[] = { 33 kModStage, 34 kModMultiInstall, 35 kModChrome, 36 kModChromeFrame, 37 kModAppHostDeprecated, // TODO(huangs): Remove by M27. 38 kModAppLauncher, 39 kModReadyMode, 40 kSfxMultiFail, 41 kSfxFull 42}; 43 44enum ModifierIndex { 45 MOD_STAGE, 46 MOD_MULTI_INSTALL, 47 MOD_CHROME, 48 MOD_CHROME_FRAME, 49 MOD_APP_HOST_DEPRECATED, // TODO(huangs): Remove by M27. 50 MOD_APP_LAUNCHER, 51 MOD_READY_MODE, 52 SFX_MULTI_FAIL, 53 SFX_FULL, 54 NUM_MODIFIERS 55}; 56 57COMPILE_ASSERT(NUM_MODIFIERS == arraysize(kModifiers), 58 kModifiers_disagrees_with_ModifierIndex_comma_they_must_match_bang); 59 60// Returns true if the modifier is found, in which case |position| holds the 61// location at which the modifier was found. The number of characters in the 62// modifier is returned in |length|, if non-NULL. 63bool FindModifier(ModifierIndex index, 64 const std::wstring& ap_value, 65 std::wstring::size_type* position, 66 std::wstring::size_type* length) { 67 DCHECK(position != NULL); 68 std::wstring::size_type mod_position = std::wstring::npos; 69 std::wstring::size_type mod_length = 70 std::wstring::traits_type::length(kModifiers[index]); 71 const bool mod_takes_arg = (kModifiers[index][mod_length - 1] == L':'); 72 std::wstring::size_type pos = 0; 73 do { 74 mod_position = ap_value.find(kModifiers[index], pos, mod_length); 75 if (mod_position == std::wstring::npos) 76 return false; // Modifier not found. 77 pos = mod_position + mod_length; 78 // Modifiers that take an argument gobble up to the next separator or to the 79 // end. 80 if (mod_takes_arg) { 81 pos = ap_value.find(L'-', pos); 82 if (pos == std::wstring::npos) 83 pos = ap_value.size(); 84 break; 85 } 86 // Regular modifiers must be followed by '-' or the end of the string. 87 } while (pos != ap_value.size() && ap_value[pos] != L'-'); 88 DCHECK_NE(mod_position, std::wstring::npos); 89 *position = mod_position; 90 if (length != NULL) 91 *length = pos - mod_position; 92 return true; 93} 94 95bool HasModifier(ModifierIndex index, const std::wstring& ap_value) { 96 DCHECK(index >= 0 && index < NUM_MODIFIERS); 97 std::wstring::size_type position; 98 return FindModifier(index, ap_value, &position, NULL); 99} 100 101std::wstring::size_type FindInsertionPoint(ModifierIndex index, 102 const std::wstring& ap_value) { 103 // Return the location of the next modifier. 104 std::wstring::size_type result; 105 106 for (int scan = index + 1; scan < NUM_MODIFIERS; ++scan) { 107 if (FindModifier(static_cast<ModifierIndex>(scan), ap_value, &result, NULL)) 108 return result; 109 } 110 111 return ap_value.size(); 112} 113 114// Returns true if |ap_value| is modified. 115bool SetModifier(ModifierIndex index, bool set, std::wstring* ap_value) { 116 DCHECK(index >= 0 && index < NUM_MODIFIERS); 117 DCHECK(ap_value); 118 std::wstring::size_type position; 119 std::wstring::size_type length; 120 bool have_modifier = FindModifier(index, *ap_value, &position, &length); 121 if (set) { 122 if (!have_modifier) { 123 ap_value->insert(FindInsertionPoint(index, *ap_value), kModifiers[index]); 124 return true; 125 } 126 } else { 127 if (have_modifier) { 128 ap_value->erase(position, length); 129 return true; 130 } 131 } 132 return false; 133} 134 135} // namespace 136 137namespace installer { 138 139bool ChannelInfo::Initialize(const RegKey& key) { 140 LONG result = key.ReadValue(google_update::kRegApField, &value_); 141 return result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND || 142 result == ERROR_INVALID_HANDLE; 143} 144 145bool ChannelInfo::Write(RegKey* key) const { 146 DCHECK(key); 147 // Google Update deletes the value when it is empty, so we may as well, too. 148 LONG result = value_.empty() ? 149 key->DeleteValue(google_update::kRegApField) : 150 key->WriteValue(google_update::kRegApField, value_.c_str()); 151 if (result != ERROR_SUCCESS) { 152 LOG(ERROR) << "Failed writing channel info; result: " << result; 153 return false; 154 } 155 return true; 156} 157 158bool ChannelInfo::GetChannelName(std::wstring* channel_name) const { 159 DCHECK(channel_name); 160 if (value_.empty()) { 161 channel_name->erase(); 162 return true; 163 } else { 164 for (const wchar_t* const* scan = &kChannels[0], 165 *const* end = &kChannels[arraysize(kChannels)]; scan != end; 166 ++scan) { 167 if (value_.find(*scan) != std::wstring::npos) { 168 channel_name->assign(*scan); 169 return true; 170 } 171 } 172 // There may be modifiers present. Strip them off and see if we're left 173 // with the empty string (stable channel). 174 std::wstring tmp_value = value_; 175 for (int i = 0; i != NUM_MODIFIERS; ++i) { 176 SetModifier(static_cast<ModifierIndex>(i), false, &tmp_value); 177 } 178 if (tmp_value.empty()) { 179 channel_name->erase(); 180 return true; 181 } 182 } 183 184 return false; 185} 186 187bool ChannelInfo::IsChrome() const { 188 return HasModifier(MOD_CHROME, value_); 189} 190 191bool ChannelInfo::SetChrome(bool value) { 192 return SetModifier(MOD_CHROME, value, &value_); 193} 194 195bool ChannelInfo::IsChromeFrame() const { 196 return HasModifier(MOD_CHROME_FRAME, value_); 197} 198 199bool ChannelInfo::SetChromeFrame(bool value) { 200 return SetModifier(MOD_CHROME_FRAME, value, &value_); 201} 202 203bool ChannelInfo::IsAppLauncher() const { 204 return HasModifier(MOD_APP_LAUNCHER, value_); 205} 206 207bool ChannelInfo::SetAppLauncher(bool value) { 208 // Unconditionally remove -apphost since it has been deprecated. 209 bool changed_app_host = SetModifier(MOD_APP_HOST_DEPRECATED, false, &value_); 210 bool changed_app_launcher = SetModifier(MOD_APP_LAUNCHER, value, &value_); 211 return changed_app_host || changed_app_launcher; 212} 213 214bool ChannelInfo::IsMultiInstall() const { 215 return HasModifier(MOD_MULTI_INSTALL, value_); 216} 217 218bool ChannelInfo::SetMultiInstall(bool value) { 219 return SetModifier(MOD_MULTI_INSTALL, value, &value_); 220} 221 222bool ChannelInfo::IsReadyMode() const { 223 return HasModifier(MOD_READY_MODE, value_); 224} 225 226bool ChannelInfo::SetReadyMode(bool value) { 227 return SetModifier(MOD_READY_MODE, value, &value_); 228} 229 230bool ChannelInfo::SetStage(const wchar_t* stage) { 231 std::wstring::size_type position; 232 std::wstring::size_type length; 233 bool have_modifier = FindModifier(MOD_STAGE, value_, &position, &length); 234 if (stage != NULL && *stage != L'\0') { 235 std::wstring stage_str(kModStage); 236 stage_str.append(stage); 237 if (!have_modifier) { 238 value_.insert(FindInsertionPoint(MOD_STAGE, value_), stage_str); 239 return true; 240 } 241 if (value_.compare(position, length, stage_str) != 0) { 242 value_.replace(position, length, stage_str); 243 return true; 244 } 245 } else { 246 if (have_modifier) { 247 value_.erase(position, length); 248 return true; 249 } 250 } 251 return false; 252} 253 254std::wstring ChannelInfo::GetStage() const { 255 std::wstring::size_type position; 256 std::wstring::size_type length; 257 258 if (FindModifier(MOD_STAGE, value_, &position, &length)) { 259 // Return the portion after the prefix. 260 std::wstring::size_type pfx_length = 261 std::wstring::traits_type::length(kModStage); 262 DCHECK_LE(pfx_length, length); 263 return value_.substr(position + pfx_length, length - pfx_length); 264 } 265 return std::wstring(); 266} 267 268bool ChannelInfo::HasFullSuffix() const { 269 return HasModifier(SFX_FULL, value_); 270} 271 272bool ChannelInfo::SetFullSuffix(bool value) { 273 return SetModifier(SFX_FULL, value, &value_); 274} 275 276bool ChannelInfo::HasMultiFailSuffix() const { 277 return HasModifier(SFX_MULTI_FAIL, value_); 278} 279 280bool ChannelInfo::SetMultiFailSuffix(bool value) { 281 return SetModifier(SFX_MULTI_FAIL, value, &value_); 282} 283 284bool ChannelInfo::RemoveAllModifiersAndSuffixes() { 285 bool modified = false; 286 287 for (int scan = 0; scan < NUM_MODIFIERS; ++scan) { 288 ModifierIndex index = static_cast<ModifierIndex>(scan); 289 modified = SetModifier(index, false, &value_) || modified; 290 } 291 292 return modified; 293} 294 295} // namespace installer 296