• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
16 using base::ASCIIToWide;
17 
18 namespace rlz_lib {
19 
20 namespace {
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 //
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";
48 
GetWideProductName(Product product)49 std::wstring GetWideProductName(Product product) {
50   return ASCIIToWide(GetProductName(product));
51 }
52 
AppendBrandToString(std::string * str)53 void 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.
GetRegKey(const char * name,REGSAM access,base::win::RegKey * key)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 
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 
GetPingTimesRegKey(REGSAM access,base::win::RegKey * key)77 bool GetPingTimesRegKey(REGSAM access, base::win::RegKey* key) {
78   return GetRegKey(kPingTimesSubkeyName, access, key);
79 }
80 
81 
GetEventsRegKey(const char * event_type,const rlz_lib::Product * product,REGSAM access,base::win::RegKey * key)82 bool 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 
GetAccessPointRlzsRegKey(REGSAM access,base::win::RegKey * key)110 bool GetAccessPointRlzsRegKey(REGSAM access, base::win::RegKey* key) {
111   return GetRegKey(kRlzsSubkeyName, access, key);
112 }
113 
ClearAllProductEventValues(rlz_lib::Product product,const char * key)114 bool 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.
DeleteKeyIfEmpty(HKEY root_key,const wchar_t * key_name)136 bool 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
GetWideLibKeyName()162 std::wstring RlzValueStoreRegistry::GetWideLibKeyName() {
163   return ASCIIToWide(kLibKeyName);
164 }
165 
HasAccess(AccessType type)166 bool RlzValueStoreRegistry::HasAccess(AccessType type) {
167   return HasUserKeyAccess(type == kWriteAccess);
168 }
169 
WritePingTime(Product product,int64 time)170 bool 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 
ReadPingTime(Product product,int64 * time)178 bool 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 
ClearPingTime(Product product)185 bool 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 
WriteAccessPointRlz(AccessPoint access_point,const char * new_rlz)204 bool 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 
ReadAccessPointRlz(AccessPoint access_point,char * rlz,size_t rlz_size)221 bool 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 
ClearAccessPointRlz(AccessPoint access_point)242 bool 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 
AddProductEvent(Product product,const char * event_rlz)263 bool 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 
ReadProductEvents(Product product,std::vector<std::string> * events)276 bool 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 
ClearProductEvent(Product product,const char * event_rlz)302 bool 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 
ClearAllProductEvents(Product product)319 bool RlzValueStoreRegistry::ClearAllProductEvents(Product product) {
320   return ClearAllProductEventValues(product, kEventsSubkeyName);
321 }
322 
AddStatefulEvent(Product product,const char * event_rlz)323 bool 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 
IsStatefulEvent(Product product,const char * event_rlz)337 bool 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 
ClearAllStatefulEvents(Product product)346 bool RlzValueStoreRegistry::ClearAllStatefulEvents(Product product) {
347   return ClearAllProductEventValues(product, kStatefulEventsSubkeyName);
348 }
349 
CollectGarbage()350 void 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 
ScopedRlzValueStoreLock()374 ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() {
375   if (!lock_.failed())
376     store_.reset(new RlzValueStoreRegistry);
377 }
378 
~ScopedRlzValueStoreLock()379 ScopedRlzValueStoreLock::~ScopedRlzValueStoreLock() {
380 }
381 
GetStore()382 RlzValueStore* ScopedRlzValueStoreLock::GetStore() {
383   return store_.get();
384 }
385 
386 }  // namespace rlz_lib
387