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