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, &reg_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, &reg_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