rlz_value_store_registry.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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 "rlz/win/lib/rlz_value_store_registry.h" 6 7#include "base/strings/stringprintf.h" 8#include "base/strings/utf_string_conversions.h" 9#include "base/win/registry.h" 10#include "rlz/lib/assert.h" 11#include "rlz/lib/lib_values.h" 12#include "rlz/lib/rlz_lib.h" 13#include "rlz/lib/string_utils.h" 14#include "rlz/win/lib/registry_util.h" 15 16namespace rlz_lib { 17 18namespace { 19 20// 21// Registry keys: 22// 23// RLZ's are stored as: 24// <AccessPointName> = <RLZ value> @ kRootKey\kLibKeyName\kRlzsSubkeyName. 25// 26// Events are stored as: 27// <AccessPointName><EventName> = 1 @ 28// HKCU\kLibKeyName\kEventsSubkeyName\GetProductName(product). 29// 30// The OEM Deal Confirmation Code (DCC) is stored as 31// kDccValueName = <DCC value> @ HKLM\kLibKeyName 32// 33// The last ping time, per product is stored as: 34// GetProductName(product) = <last ping time> @ 35// HKCU\kLibKeyName\kPingTimesSubkeyName. 36// 37// The server does not care about any of these constants. 38// 39const char kLibKeyName[] = "Software\\Google\\Common\\Rlz"; 40const wchar_t kGoogleKeyName[] = L"Software\\Google"; 41const wchar_t kGoogleCommonKeyName[] = L"Software\\Google\\Common"; 42const char kRlzsSubkeyName[] = "RLZs"; 43const char kEventsSubkeyName[] = "Events"; 44const char kStatefulEventsSubkeyName[] = "StatefulEvents"; 45const char kPingTimesSubkeyName[] = "PTimes"; 46 47std::wstring GetWideProductName(Product product) { 48 return ASCIIToWide(GetProductName(product)); 49} 50 51void AppendBrandToString(std::string* str) { 52 std::string brand(SupplementaryBranding::GetBrand()); 53 if (!brand.empty()) 54 base::StringAppendF(str, "\\_%s", brand.c_str()); 55} 56 57// Function to get the specific registry keys. 58bool GetRegKey(const char* name, REGSAM access, base::win::RegKey* key) { 59 std::string key_location; 60 base::StringAppendF(&key_location, "%s\\%s", kLibKeyName, name); 61 AppendBrandToString(&key_location); 62 63 LONG ret = ERROR_SUCCESS; 64 if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK)) { 65 ret = key->Create(HKEY_CURRENT_USER, ASCIIToWide(key_location).c_str(), 66 access); 67 } else { 68 ret = key->Open(HKEY_CURRENT_USER, ASCIIToWide(key_location).c_str(), 69 access); 70 } 71 72 return ret == ERROR_SUCCESS; 73} 74 75bool GetPingTimesRegKey(REGSAM access, base::win::RegKey* key) { 76 return GetRegKey(kPingTimesSubkeyName, access, key); 77} 78 79 80bool GetEventsRegKey(const char* event_type, 81 const rlz_lib::Product* product, 82 REGSAM access, base::win::RegKey* key) { 83 std::string key_location; 84 base::StringAppendF(&key_location, "%s\\%s", kLibKeyName, 85 event_type); 86 AppendBrandToString(&key_location); 87 88 if (product != NULL) { 89 std::string product_name = GetProductName(*product); 90 if (product_name.empty()) 91 return false; 92 93 base::StringAppendF(&key_location, "\\%s", product_name.c_str()); 94 } 95 96 LONG ret = ERROR_SUCCESS; 97 if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK)) { 98 ret = key->Create(HKEY_CURRENT_USER, ASCIIToWide(key_location).c_str(), 99 access); 100 } else { 101 ret = key->Open(HKEY_CURRENT_USER, ASCIIToWide(key_location).c_str(), 102 access); 103 } 104 105 return ret == ERROR_SUCCESS; 106} 107 108bool GetAccessPointRlzsRegKey(REGSAM access, base::win::RegKey* key) { 109 return GetRegKey(kRlzsSubkeyName, access, key); 110} 111 112bool ClearAllProductEventValues(rlz_lib::Product product, const char* key) { 113 std::wstring product_name = GetWideProductName(product); 114 if (product_name.empty()) 115 return false; 116 117 base::win::RegKey reg_key; 118 GetEventsRegKey(key, NULL, KEY_WRITE, ®_key); 119 reg_key.DeleteKey(product_name.c_str()); 120 121 // Verify that the value no longer exists. 122 base::win::RegKey product_events( 123 reg_key.Handle(), product_name.c_str(), KEY_READ); 124 if (product_events.Valid()) { 125 ASSERT_STRING("ClearAllProductEvents: Key deletion failed"); 126 return false; 127 } 128 129 return true; 130} 131 132// Deletes a registry key if it exists and has no subkeys or values. 133// TODO: Move this to a registry_utils file and add unittest. 134bool DeleteKeyIfEmpty(HKEY root_key, const wchar_t* key_name) { 135 if (!key_name) { 136 ASSERT_STRING("DeleteKeyIfEmpty: key_name is NULL"); 137 return false; 138 } else { // Scope needed for RegKey 139 base::win::RegKey key(root_key, key_name, KEY_READ); 140 if (!key.Valid()) 141 return true; // Key does not exist - nothing to do. 142 143 base::win::RegistryKeyIterator key_iter(root_key, key_name); 144 if (key_iter.SubkeyCount() > 0) 145 return true; // Not empty, so nothing to do 146 147 base::win::RegistryValueIterator value_iter(root_key, key_name); 148 if (value_iter.ValueCount() > 0) 149 return true; // Not empty, so nothing to do 150 } 151 152 // The key is empty - delete it now. 153 base::win::RegKey key(root_key, L"", KEY_WRITE); 154 return key.DeleteKey(key_name) == ERROR_SUCCESS; 155} 156 157} // namespace 158 159// static 160std::wstring RlzValueStoreRegistry::GetWideLibKeyName() { 161 return ASCIIToWide(kLibKeyName); 162} 163 164bool RlzValueStoreRegistry::HasAccess(AccessType type) { 165 return HasUserKeyAccess(type == kWriteAccess); 166} 167 168bool RlzValueStoreRegistry::WritePingTime(Product product, int64 time) { 169 base::win::RegKey key; 170 std::wstring product_name = GetWideProductName(product); 171 return GetPingTimesRegKey(KEY_WRITE, &key) && 172 key.WriteValue(product_name.c_str(), &time, sizeof(time), 173 REG_QWORD) == ERROR_SUCCESS; 174} 175 176bool RlzValueStoreRegistry::ReadPingTime(Product product, int64* time) { 177 base::win::RegKey key; 178 std::wstring product_name = GetWideProductName(product); 179 return GetPingTimesRegKey(KEY_READ, &key) && 180 key.ReadInt64(product_name.c_str(), time) == ERROR_SUCCESS; 181} 182 183bool RlzValueStoreRegistry::ClearPingTime(Product product) { 184 base::win::RegKey key; 185 GetPingTimesRegKey(KEY_WRITE, &key); 186 187 std::wstring product_name = GetWideProductName(product); 188 key.DeleteValue(product_name.c_str()); 189 190 // Verify deletion. 191 uint64 value; 192 DWORD size = sizeof(value); 193 if (key.ReadValue( 194 product_name.c_str(), &value, &size, NULL) == ERROR_SUCCESS) { 195 ASSERT_STRING("RlzValueStoreRegistry::ClearPingTime: Failed to delete."); 196 return false; 197 } 198 199 return true; 200} 201 202bool RlzValueStoreRegistry::WriteAccessPointRlz(AccessPoint access_point, 203 const char* new_rlz) { 204 const char* access_point_name = GetAccessPointName(access_point); 205 if (!access_point_name) 206 return false; 207 208 std::wstring access_point_name_wide(ASCIIToWide(access_point_name)); 209 base::win::RegKey key; 210 GetAccessPointRlzsRegKey(KEY_WRITE, &key); 211 212 if (!RegKeyWriteValue(key, access_point_name_wide.c_str(), new_rlz)) { 213 ASSERT_STRING("SetAccessPointRlz: Could not write the new RLZ value"); 214 return false; 215 } 216 return true; 217} 218 219bool RlzValueStoreRegistry::ReadAccessPointRlz(AccessPoint access_point, 220 char* rlz, 221 size_t rlz_size) { 222 const char* access_point_name = GetAccessPointName(access_point); 223 if (!access_point_name) 224 return false; 225 226 size_t size = rlz_size; 227 base::win::RegKey key; 228 GetAccessPointRlzsRegKey(KEY_READ, &key); 229 if (!RegKeyReadValue(key, ASCIIToWide(access_point_name).c_str(), 230 rlz, &size)) { 231 rlz[0] = 0; 232 if (size > rlz_size) { 233 ASSERT_STRING("GetAccessPointRlz: Insufficient buffer size"); 234 return false; 235 } 236 } 237 return true; 238} 239 240bool RlzValueStoreRegistry::ClearAccessPointRlz(AccessPoint access_point) { 241 const char* access_point_name = GetAccessPointName(access_point); 242 if (!access_point_name) 243 return false; 244 245 std::wstring access_point_name_wide(ASCIIToWide(access_point_name)); 246 base::win::RegKey key; 247 GetAccessPointRlzsRegKey(KEY_WRITE, &key); 248 249 key.DeleteValue(access_point_name_wide.c_str()); 250 251 // Verify deletion. 252 DWORD value; 253 if (key.ReadValueDW(access_point_name_wide.c_str(), &value) == 254 ERROR_SUCCESS) { 255 ASSERT_STRING("SetAccessPointRlz: Could not clear the RLZ value."); 256 return false; 257 } 258 return true; 259} 260 261bool RlzValueStoreRegistry::AddProductEvent(Product product, 262 const char* event_rlz) { 263 std::wstring event_rlz_wide(ASCIIToWide(event_rlz)); 264 base::win::RegKey reg_key; 265 GetEventsRegKey(kEventsSubkeyName, &product, KEY_WRITE, ®_key); 266 if (reg_key.WriteValue(event_rlz_wide.c_str(), 1) != ERROR_SUCCESS) { 267 ASSERT_STRING("AddProductEvent: Could not write the new event value"); 268 return false; 269 } 270 271 return true; 272} 273 274bool RlzValueStoreRegistry::ReadProductEvents(Product product, 275 std::vector<std::string>* events) { 276 // Open the events key. 277 base::win::RegKey events_key; 278 GetEventsRegKey(kEventsSubkeyName, &product, KEY_READ, &events_key); 279 if (!events_key.Valid()) 280 return false; 281 282 // Append the events to the buffer. 283 int num_values = 0; 284 LONG result = ERROR_SUCCESS; 285 for (num_values = 0; result == ERROR_SUCCESS; ++num_values) { 286 // Max 32767 bytes according to MSDN, but we never use that much. 287 const size_t kMaxValueNameLength = 2048; 288 char buffer[kMaxValueNameLength]; 289 DWORD size = arraysize(buffer); 290 291 result = RegEnumValueA(events_key.Handle(), num_values, buffer, &size, 292 NULL, NULL, NULL, NULL); 293 if (result == ERROR_SUCCESS) 294 events->push_back(std::string(buffer)); 295 } 296 297 return result == ERROR_NO_MORE_ITEMS; 298} 299 300bool RlzValueStoreRegistry::ClearProductEvent(Product product, 301 const char* event_rlz) { 302 std::wstring event_rlz_wide(ASCIIToWide(event_rlz)); 303 base::win::RegKey key; 304 GetEventsRegKey(kEventsSubkeyName, &product, KEY_WRITE, &key); 305 key.DeleteValue(event_rlz_wide.c_str()); 306 307 // Verify deletion. 308 DWORD value; 309 if (key.ReadValueDW(event_rlz_wide.c_str(), &value) == ERROR_SUCCESS) { 310 ASSERT_STRING("ClearProductEvent: Could not delete the event value."); 311 return false; 312 } 313 314 return true; 315} 316 317bool RlzValueStoreRegistry::ClearAllProductEvents(Product product) { 318 return ClearAllProductEventValues(product, kEventsSubkeyName); 319} 320 321bool RlzValueStoreRegistry::AddStatefulEvent(Product product, 322 const char* event_rlz) { 323 base::win::RegKey key; 324 std::wstring event_rlz_wide(ASCIIToWide(event_rlz)); 325 if (!GetEventsRegKey(kStatefulEventsSubkeyName, &product, KEY_WRITE, &key) || 326 key.WriteValue(event_rlz_wide.c_str(), 1) != ERROR_SUCCESS) { 327 ASSERT_STRING( 328 "AddStatefulEvent: Could not write the new stateful event"); 329 return false; 330 } 331 332 return true; 333} 334 335bool RlzValueStoreRegistry::IsStatefulEvent(Product product, 336 const char* event_rlz) { 337 DWORD value; 338 base::win::RegKey key; 339 GetEventsRegKey(kStatefulEventsSubkeyName, &product, KEY_READ, &key); 340 std::wstring event_rlz_wide(ASCIIToWide(event_rlz)); 341 return key.ReadValueDW(event_rlz_wide.c_str(), &value) == ERROR_SUCCESS; 342} 343 344bool RlzValueStoreRegistry::ClearAllStatefulEvents(Product product) { 345 return ClearAllProductEventValues(product, kStatefulEventsSubkeyName); 346} 347 348void RlzValueStoreRegistry::CollectGarbage() { 349 // Delete each of the known subkeys if empty. 350 const char* subkeys[] = { 351 kRlzsSubkeyName, 352 kEventsSubkeyName, 353 kStatefulEventsSubkeyName, 354 kPingTimesSubkeyName 355 }; 356 357 for (int i = 0; i < arraysize(subkeys); i++) { 358 std::string subkey_name; 359 base::StringAppendF(&subkey_name, "%s\\%s", kLibKeyName, subkeys[i]); 360 AppendBrandToString(&subkey_name); 361 362 VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER, 363 ASCIIToWide(subkey_name).c_str())); 364 } 365 366 // Delete the library key and its parents too now if empty. 367 VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER, GetWideLibKeyName().c_str())); 368 VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER, kGoogleCommonKeyName)); 369 VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER, kGoogleKeyName)); 370} 371 372ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() { 373 if (!lock_.failed()) 374 store_.reset(new RlzValueStoreRegistry); 375} 376 377ScopedRlzValueStoreLock::~ScopedRlzValueStoreLock() { 378} 379 380RlzValueStore* ScopedRlzValueStoreLock::GetStore() { 381 return store_.get(); 382} 383 384} // namespace rlz_lib 385