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.
5 #include "rlz/win/lib/rlz_value_store_registry.h"
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"
16 using base::ASCIIToUTF16
;
25 // RLZ's are stored as:
26 // <AccessPointName> = <RLZ value> @ kRootKey\kLibKeyName\kRlzsSubkeyName.
28 // Events are stored as:
29 // <AccessPointName><EventName> = 1 @
30 // HKCU\kLibKeyName\kEventsSubkeyName\GetProductName(product).
32 // The OEM Deal Confirmation Code (DCC) is stored as
33 // kDccValueName = <DCC value> @ HKLM\kLibKeyName
35 // The last ping time, per product is stored as:
36 // GetProductName(product) = <last ping time> @
37 // HKCU\kLibKeyName\kPingTimesSubkeyName.
39 // The server does not care about any of these constants.
41 const char kLibKeyName
[] = "Software\\Google\\Common\\Rlz";
42 const wchar_t kGoogleKeyName
[] = L
"Software\\Google";
43 const wchar_t kGoogleCommonKeyName
[] = L
"Software\\Google\\Common";
44 const char kRlzsSubkeyName
[] = "RLZs";
45 const char kEventsSubkeyName
[] = "Events";
46 const char kStatefulEventsSubkeyName
[] = "StatefulEvents";
47 const char kPingTimesSubkeyName
[] = "PTimes";
49 std::wstring
GetWideProductName(Product product
) {
50 return ASCIIToUTF16(GetProductName(product
));
53 void AppendBrandToString(std::string
* str
) {
54 std::string
brand(SupplementaryBranding::GetBrand());
56 base::StringAppendF(str
, "\\_%s", brand
.c_str());
59 // Function to get the specific registry keys.
60 bool GetRegKey(const char* name
, REGSAM access
, base::win::RegKey
* key
) {
61 std::string key_location
;
62 base::StringAppendF(&key_location
, "%s\\%s", kLibKeyName
, name
);
63 AppendBrandToString(&key_location
);
64 base::string16 key_location16
= ASCIIToUTF16(key_location
);
67 if (access
& (KEY_SET_VALUE
| KEY_CREATE_SUB_KEY
| KEY_CREATE_LINK
))
68 ret
= key
->Create(HKEY_CURRENT_USER
, key_location16
.c_str(), access
);
70 ret
= key
->Open(HKEY_CURRENT_USER
, key_location16
.c_str(), access
);
71 return ret
== ERROR_SUCCESS
;
74 bool GetPingTimesRegKey(REGSAM access
, base::win::RegKey
* key
) {
75 return GetRegKey(kPingTimesSubkeyName
, access
, key
);
79 bool GetEventsRegKey(const char* event_type
,
80 const rlz_lib::Product
* product
,
81 REGSAM access
, base::win::RegKey
* key
) {
82 std::string key_location
;
83 base::StringAppendF(&key_location
, "%s\\%s", kLibKeyName
,
85 AppendBrandToString(&key_location
);
87 if (product
!= NULL
) {
88 std::string product_name
= GetProductName(*product
);
89 if (product_name
.empty())
92 base::StringAppendF(&key_location
, "\\%s", product_name
.c_str());
94 base::string16 key_location16
= ASCIIToUTF16(key_location
);
97 if (access
& (KEY_SET_VALUE
| KEY_CREATE_SUB_KEY
| KEY_CREATE_LINK
))
98 ret
= key
->Create(HKEY_CURRENT_USER
, key_location16
.c_str(), access
);
100 ret
= key
->Open(HKEY_CURRENT_USER
, key_location16
.c_str(), access
);
101 return ret
== ERROR_SUCCESS
;
104 bool GetAccessPointRlzsRegKey(REGSAM access
, base::win::RegKey
* key
) {
105 return GetRegKey(kRlzsSubkeyName
, access
, key
);
108 bool ClearAllProductEventValues(rlz_lib::Product product
, const char* key
) {
109 std::wstring product_name
= GetWideProductName(product
);
110 if (product_name
.empty())
113 base::win::RegKey reg_key
;
114 GetEventsRegKey(key
, NULL
, KEY_WRITE
, ®_key
);
115 reg_key
.DeleteKey(product_name
.c_str());
117 // Verify that the value no longer exists.
118 base::win::RegKey
product_events(
119 reg_key
.Handle(), product_name
.c_str(), KEY_READ
);
120 if (product_events
.Valid()) {
121 ASSERT_STRING("ClearAllProductEvents: Key deletion failed");
128 // Deletes a registry key if it exists and has no subkeys or values.
129 // TODO: Move this to a registry_utils file and add unittest.
130 bool DeleteKeyIfEmpty(HKEY root_key
, const wchar_t* key_name
) {
132 ASSERT_STRING("DeleteKeyIfEmpty: key_name is NULL");
134 } else { // Scope needed for RegKey
135 base::win::RegKey
key(root_key
, key_name
, KEY_READ
);
137 return true; // Key does not exist - nothing to do.
139 base::win::RegistryKeyIterator
key_iter(root_key
, key_name
);
140 if (key_iter
.SubkeyCount() > 0)
141 return true; // Not empty, so nothing to do
143 base::win::RegistryValueIterator
value_iter(root_key
, key_name
);
144 if (value_iter
.ValueCount() > 0)
145 return true; // Not empty, so nothing to do
148 // The key is empty - delete it now.
149 base::win::RegKey
key(root_key
, L
"", KEY_WRITE
);
150 return key
.DeleteKey(key_name
) == ERROR_SUCCESS
;
156 std::wstring
RlzValueStoreRegistry::GetWideLibKeyName() {
157 return ASCIIToUTF16(kLibKeyName
);
160 bool RlzValueStoreRegistry::HasAccess(AccessType type
) {
161 return HasUserKeyAccess(type
== kWriteAccess
);
164 bool RlzValueStoreRegistry::WritePingTime(Product product
, int64 time
) {
165 base::win::RegKey key
;
166 std::wstring product_name
= GetWideProductName(product
);
167 return GetPingTimesRegKey(KEY_WRITE
, &key
) &&
168 key
.WriteValue(product_name
.c_str(), &time
, sizeof(time
),
169 REG_QWORD
) == ERROR_SUCCESS
;
172 bool RlzValueStoreRegistry::ReadPingTime(Product product
, int64
* time
) {
173 base::win::RegKey key
;
174 std::wstring product_name
= GetWideProductName(product
);
175 return GetPingTimesRegKey(KEY_READ
, &key
) &&
176 key
.ReadInt64(product_name
.c_str(), time
) == ERROR_SUCCESS
;
179 bool RlzValueStoreRegistry::ClearPingTime(Product product
) {
180 base::win::RegKey key
;
181 GetPingTimesRegKey(KEY_WRITE
, &key
);
183 std::wstring product_name
= GetWideProductName(product
);
184 key
.DeleteValue(product_name
.c_str());
188 DWORD size
= sizeof(value
);
190 product_name
.c_str(), &value
, &size
, NULL
) == ERROR_SUCCESS
) {
191 ASSERT_STRING("RlzValueStoreRegistry::ClearPingTime: Failed to delete.");
198 bool RlzValueStoreRegistry::WriteAccessPointRlz(AccessPoint access_point
,
199 const char* new_rlz
) {
200 const char* access_point_name
= GetAccessPointName(access_point
);
201 if (!access_point_name
)
204 base::string16
access_point_name16(ASCIIToUTF16(access_point_name
));
205 base::win::RegKey key
;
206 GetAccessPointRlzsRegKey(KEY_WRITE
, &key
);
208 if (!RegKeyWriteValue(&key
, access_point_name16
.c_str(), new_rlz
)) {
209 ASSERT_STRING("SetAccessPointRlz: Could not write the new RLZ value");
215 bool RlzValueStoreRegistry::ReadAccessPointRlz(AccessPoint access_point
,
218 const char* access_point_name
= GetAccessPointName(access_point
);
219 if (!access_point_name
)
222 size_t size
= rlz_size
;
223 base::win::RegKey key
;
224 GetAccessPointRlzsRegKey(KEY_READ
, &key
);
225 base::string16 access_point_name16
= ASCIIToUTF16(access_point_name
);
226 if (!RegKeyReadValue(key
, access_point_name16
.c_str(), rlz
, &size
)) {
228 if (size
> rlz_size
) {
229 ASSERT_STRING("GetAccessPointRlz: Insufficient buffer size");
236 bool RlzValueStoreRegistry::ClearAccessPointRlz(AccessPoint access_point
) {
237 const char* access_point_name
= GetAccessPointName(access_point
);
238 if (!access_point_name
)
241 base::string16
access_point_name16(ASCIIToUTF16(access_point_name
));
242 base::win::RegKey key
;
243 GetAccessPointRlzsRegKey(KEY_WRITE
, &key
);
245 key
.DeleteValue(access_point_name16
.c_str());
249 if (key
.ReadValueDW(access_point_name16
.c_str(), &value
) == ERROR_SUCCESS
) {
250 ASSERT_STRING("SetAccessPointRlz: Could not clear the RLZ value.");
256 bool RlzValueStoreRegistry::AddProductEvent(Product product
,
257 const char* event_rlz
) {
258 base::string16
event_rlz16(ASCIIToUTF16(event_rlz
));
259 base::win::RegKey reg_key
;
260 GetEventsRegKey(kEventsSubkeyName
, &product
, KEY_WRITE
, ®_key
);
261 if (reg_key
.WriteValue(event_rlz16
.c_str(), 1) != ERROR_SUCCESS
) {
262 ASSERT_STRING("AddProductEvent: Could not write the new event value");
269 bool RlzValueStoreRegistry::ReadProductEvents(Product product
,
270 std::vector
<std::string
>* events
) {
271 // Open the events key.
272 base::win::RegKey events_key
;
273 GetEventsRegKey(kEventsSubkeyName
, &product
, KEY_READ
, &events_key
);
274 if (!events_key
.Valid())
277 // Append the events to the buffer.
279 LONG result
= ERROR_SUCCESS
;
280 for (num_values
= 0; result
== ERROR_SUCCESS
; ++num_values
) {
281 // Max 32767 bytes according to MSDN, but we never use that much.
282 const size_t kMaxValueNameLength
= 2048;
283 char buffer
[kMaxValueNameLength
];
284 DWORD size
= arraysize(buffer
);
286 result
= RegEnumValueA(events_key
.Handle(), num_values
, buffer
, &size
,
287 NULL
, NULL
, NULL
, NULL
);
288 if (result
== ERROR_SUCCESS
)
289 events
->push_back(std::string(buffer
));
292 return result
== ERROR_NO_MORE_ITEMS
;
295 bool RlzValueStoreRegistry::ClearProductEvent(Product product
,
296 const char* event_rlz
) {
297 base::string16
event_rlz16(ASCIIToUTF16(event_rlz
));
298 base::win::RegKey key
;
299 GetEventsRegKey(kEventsSubkeyName
, &product
, KEY_WRITE
, &key
);
300 key
.DeleteValue(event_rlz16
.c_str());
304 if (key
.ReadValueDW(event_rlz16
.c_str(), &value
) == ERROR_SUCCESS
) {
305 ASSERT_STRING("ClearProductEvent: Could not delete the event value.");
312 bool RlzValueStoreRegistry::ClearAllProductEvents(Product product
) {
313 return ClearAllProductEventValues(product
, kEventsSubkeyName
);
316 bool RlzValueStoreRegistry::AddStatefulEvent(Product product
,
317 const char* event_rlz
) {
318 base::win::RegKey key
;
319 base::string16
event_rlz16(ASCIIToUTF16(event_rlz
));
320 if (!GetEventsRegKey(kStatefulEventsSubkeyName
, &product
, KEY_WRITE
, &key
) ||
321 key
.WriteValue(event_rlz16
.c_str(), 1) != ERROR_SUCCESS
) {
323 "AddStatefulEvent: Could not write the new stateful event");
330 bool RlzValueStoreRegistry::IsStatefulEvent(Product product
,
331 const char* event_rlz
) {
333 base::win::RegKey key
;
334 GetEventsRegKey(kStatefulEventsSubkeyName
, &product
, KEY_READ
, &key
);
335 base::string16
event_rlz16(ASCIIToUTF16(event_rlz
));
336 return key
.ReadValueDW(event_rlz16
.c_str(), &value
) == ERROR_SUCCESS
;
339 bool RlzValueStoreRegistry::ClearAllStatefulEvents(Product product
) {
340 return ClearAllProductEventValues(product
, kStatefulEventsSubkeyName
);
343 void RlzValueStoreRegistry::CollectGarbage() {
344 // Delete each of the known subkeys if empty.
345 const char* const subkeys
[] = {
348 kStatefulEventsSubkeyName
,
352 for (int i
= 0; i
< arraysize(subkeys
); i
++) {
353 std::string subkey_name
;
354 base::StringAppendF(&subkey_name
, "%s\\%s", kLibKeyName
, subkeys
[i
]);
355 AppendBrandToString(&subkey_name
);
356 base::string16 subkey_name16
= ASCIIToUTF16(subkey_name
);
358 VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER
, subkey_name16
.c_str()));
361 // Delete the library key and its parents too now if empty.
362 VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER
, GetWideLibKeyName().c_str()));
363 VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER
, kGoogleCommonKeyName
));
364 VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER
, kGoogleKeyName
));
367 ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() {
369 store_
.reset(new RlzValueStoreRegistry
);
372 ScopedRlzValueStoreLock::~ScopedRlzValueStoreLock() {
375 RlzValueStore
* ScopedRlzValueStoreLock::GetStore() {
379 } // namespace rlz_lib