summaryrefslogtreecommitdiff
path: root/chromium/rlz
diff options
context:
space:
mode:
authorZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
committerZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
commit679147eead574d186ebf3069647b4c23e8ccace6 (patch)
treefc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/rlz
downloadqtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz
Initial import.
Diffstat (limited to 'chromium/rlz')
-rw-r--r--chromium/rlz/DEPS70
-rw-r--r--chromium/rlz/OWNERS3
-rw-r--r--chromium/rlz/chromeos/lib/machine_id_chromeos.cc14
-rw-r--r--chromium/rlz/chromeos/lib/rlz_value_store_chromeos.cc339
-rw-r--r--chromium/rlz/chromeos/lib/rlz_value_store_chromeos.h86
-rw-r--r--chromium/rlz/examples/rlz_id.cc13
-rw-r--r--chromium/rlz/lib/assert.cc15
-rw-r--r--chromium/rlz/lib/assert.h53
-rw-r--r--chromium/rlz/lib/crc32.h17
-rw-r--r--chromium/rlz/lib/crc32_unittest.cc52
-rw-r--r--chromium/rlz/lib/crc32_wrapper.cc36
-rw-r--r--chromium/rlz/lib/crc8.cc90
-rw-r--r--chromium/rlz/lib/crc8.h24
-rw-r--r--chromium/rlz/lib/crc8_unittest.cc51
-rw-r--r--chromium/rlz/lib/financial_ping.cc362
-rw-r--r--chromium/rlz/lib/financial_ping.h64
-rw-r--r--chromium/rlz/lib/financial_ping_test.cc293
-rw-r--r--chromium/rlz/lib/lib_values.cc208
-rw-r--r--chromium/rlz/lib/lib_values.h99
-rw-r--r--chromium/rlz/lib/lib_values_unittest.cc61
-rw-r--r--chromium/rlz/lib/machine_id.cc84
-rw-r--r--chromium/rlz/lib/machine_id.h34
-rw-r--r--chromium/rlz/lib/machine_id_unittest.cc20
-rw-r--r--chromium/rlz/lib/recursive_cross_process_lock_posix.cc82
-rw-r--r--chromium/rlz/lib/recursive_cross_process_lock_posix.h45
-rw-r--r--chromium/rlz/lib/rlz_enums.h134
-rw-r--r--chromium/rlz/lib/rlz_lib.cc651
-rw-r--r--chromium/rlz/lib/rlz_lib.h335
-rw-r--r--chromium/rlz/lib/rlz_lib_clear.cc82
-rw-r--r--chromium/rlz/lib/rlz_lib_test.cc892
-rw-r--r--chromium/rlz/lib/rlz_value_store.h120
-rw-r--r--chromium/rlz/lib/string_utils.cc92
-rw-r--r--chromium/rlz/lib/string_utils.h26
-rw-r--r--chromium/rlz/lib/string_utils_unittest.cc69
-rw-r--r--chromium/rlz/mac/lib/machine_id_mac.cc148
-rw-r--r--chromium/rlz/mac/lib/rlz_value_store_mac.h80
-rw-r--r--chromium/rlz/mac/lib/rlz_value_store_mac.mm373
-rw-r--r--chromium/rlz/rlz.gyp166
-rw-r--r--chromium/rlz/win/dll/dll_main.cc15
-rw-r--r--chromium/rlz/win/dll/exports.cc108
-rw-r--r--chromium/rlz/win/lib/lib_mutex.cc67
-rw-r--r--chromium/rlz/win/lib/lib_mutex.h28
-rw-r--r--chromium/rlz/win/lib/machine_deal.cc300
-rw-r--r--chromium/rlz/win/lib/machine_deal.h61
-rw-r--r--chromium/rlz/win/lib/machine_deal_test.cc156
-rw-r--r--chromium/rlz/win/lib/machine_id_win.cc130
-rw-r--r--chromium/rlz/win/lib/process_info.cc195
-rw-r--r--chromium/rlz/win/lib/process_info.h32
-rw-r--r--chromium/rlz/win/lib/registry_util.cc77
-rw-r--r--chromium/rlz/win/lib/registry_util.h29
-rw-r--r--chromium/rlz/win/lib/rlz_lib.h56
-rw-r--r--chromium/rlz/win/lib/rlz_lib_win.cc260
-rw-r--r--chromium/rlz/win/lib/rlz_value_store_registry.cc384
-rw-r--r--chromium/rlz/win/lib/rlz_value_store_registry.h55
54 files changed, 7336 insertions, 0 deletions
diff --git a/chromium/rlz/DEPS b/chromium/rlz/DEPS
new file mode 100644
index 00000000000..aba5be2d59e
--- /dev/null
+++ b/chromium/rlz/DEPS
@@ -0,0 +1,70 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# This DEPS file exists so that it can capture the dependenciez of RLZ. This
+# allows external projects to check it out and build it independently from all
+# of chrome.
+
+vars = {
+ "chrev": "@119173"
+}
+
+deps = {
+ "src/base":
+ "http://src.chromium.org/svn/trunk/src/base" + Var("chrev"),
+
+ "src/build":
+ "http://src.chromium.org/svn/trunk/src/build" + Var("chrev"),
+
+ "src/third_party/icu":
+ "http://src.chromium.org/svn/trunk/deps/third_party/icu42" + Var("chrev"),
+
+ "src/third_party/modp_b64":
+ "http://src.chromium.org/svn/trunk/src/third_party/modp_b64" + Var("chrev"),
+
+ "src/third_party/nss":
+ "http://src.chromium.org/svn/trunk/deps/third_party/nss" + Var("chrev"),
+
+ "src/third_party/sqlite":
+ "http://src.chromium.org/svn/trunk/src/third_party/sqlite" + Var("chrev"),
+
+ "src/third_party/wtl":
+ "http://src.chromium.org/svn/trunk/src/third_party/wtl" + Var("chrev"),
+
+ "src/third_party/zlib":
+ "http://src.chromium.org/svn/trunk/src/third_party/zlib" + Var("chrev"),
+
+ "src/testing":
+ "http://src.chromium.org/svn/trunk/src/testing" + Var("chrev"),
+
+ "src/testing/gmock":
+ "http://googlemock.googlecode.com/svn/trunk@374",
+
+ "src/testing/gtest":
+ "http://googletest.googlecode.com/svn/trunk@492",
+
+ "src/tools/gyp":
+ "http://gyp.googlecode.com/svn/trunk@1233",
+
+ "src/tools/win":
+ "http://src.chromium.org/svn/trunk/src/tools/win" + Var("chrev"),
+
+ # If using rlz with chrome's networking library, add it and its dependencies
+ # here.
+}
+
+include_rules = [
+ "+build",
+ "+net", # This is only used when force_rlz_use_chrome_net=1 is passed to gyp.
+ "+third_party/zlib",
+]
+
+hooks = [
+ {
+ # A change to a .gyp, .gypi, or to GYP itself should run the generator.
+ "pattern": ".",
+ "action": ["python", "src/build/gyp_chromium", "src/rlz/rlz.gyp"],
+ }
+]
+
diff --git a/chromium/rlz/OWNERS b/chromium/rlz/OWNERS
new file mode 100644
index 00000000000..c7cd32cf7a7
--- /dev/null
+++ b/chromium/rlz/OWNERS
@@ -0,0 +1,3 @@
+rogerta@chromium.org
+gwilson@chromium.org
+thakis@chromium.org
diff --git a/chromium/rlz/chromeos/lib/machine_id_chromeos.cc b/chromium/rlz/chromeos/lib/machine_id_chromeos.cc
new file mode 100644
index 00000000000..608fd03d3d2
--- /dev/null
+++ b/chromium/rlz/chromeos/lib/machine_id_chromeos.cc
@@ -0,0 +1,14 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "rlz/lib/machine_id.h"
+
+namespace rlz_lib {
+
+bool GetRawMachineId(string16* data, int* more_data) {
+ // Machine IDs are not tracked for ChromeOS.
+ return false;
+}
+
+} // namespace rlz_lib
diff --git a/chromium/rlz/chromeos/lib/rlz_value_store_chromeos.cc b/chromium/rlz/chromeos/lib/rlz_value_store_chromeos.cc
new file mode 100644
index 00000000000..ad1407a40eb
--- /dev/null
+++ b/chromium/rlz/chromeos/lib/rlz_value_store_chromeos.cc
@@ -0,0 +1,339 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "rlz/chromeos/lib/rlz_value_store_chromeos.h"
+
+#include "base/file_util.h"
+#include "base/files/important_file_writer.h"
+#include "base/json/json_file_value_serializer.h"
+#include "base/json/json_string_value_serializer.h"
+#include "base/logging.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "rlz/lib/lib_values.h"
+#include "rlz/lib/recursive_cross_process_lock_posix.h"
+#include "rlz/lib/rlz_lib.h"
+
+namespace rlz_lib {
+
+namespace {
+
+// Product names.
+const char kProductChrome[] = "chrome";
+const char kProductOther[] = "other";
+
+// Key names.
+const char kPingTimeKey[] = "ping_time";
+const char kAccessPointKey[] = "access_points";
+const char kProductEventKey[] = "product_events";
+const char kStatefulEventKey[] = "stateful_events";
+
+// Brand name used when there is no supplementary brand name.
+const char kNoSupplementaryBrand[] = "_";
+
+// RLZ store filename.
+const base::FilePath::CharType kRLZDataFileName[] =
+ FILE_PATH_LITERAL("RLZ Data");
+
+// RLZ store lock filename
+const base::FilePath::CharType kRLZLockFileName[] =
+ FILE_PATH_LITERAL("RLZ Data.lock");
+
+// RLZ store path for testing.
+base::FilePath g_testing_rlz_store_path_;
+
+// Returns file path of the RLZ storage.
+base::FilePath GetRlzStorePath() {
+ return g_testing_rlz_store_path_.empty() ?
+ file_util::GetHomeDir().Append(kRLZDataFileName) :
+ g_testing_rlz_store_path_.Append(kRLZDataFileName);
+}
+
+// Returns file path of the RLZ storage lock file.
+base::FilePath GetRlzStoreLockPath() {
+ return g_testing_rlz_store_path_.empty() ?
+ file_util::GetHomeDir().Append(kRLZLockFileName) :
+ g_testing_rlz_store_path_.Append(kRLZLockFileName);
+}
+
+// Returns the dictionary key for storing access point-related prefs.
+std::string GetKeyName(std::string key, AccessPoint access_point) {
+ std::string brand = SupplementaryBranding::GetBrand();
+ if (brand.empty())
+ brand = kNoSupplementaryBrand;
+ return key + "." + GetAccessPointName(access_point) + "." + brand;
+}
+
+// Returns the dictionary key for storing product-related prefs.
+std::string GetKeyName(std::string key, Product product) {
+ std::string brand = SupplementaryBranding::GetBrand();
+ if (brand.empty())
+ brand = kNoSupplementaryBrand;
+ return key + "." + GetProductName(product) + "." + brand;
+}
+
+} // namespace
+
+RlzValueStoreChromeOS::RlzValueStoreChromeOS(const base::FilePath& store_path)
+ : rlz_store_(new base::DictionaryValue),
+ store_path_(store_path),
+ read_only_(true) {
+ ReadStore();
+}
+
+RlzValueStoreChromeOS::~RlzValueStoreChromeOS() {
+ WriteStore();
+}
+
+bool RlzValueStoreChromeOS::HasAccess(AccessType type) {
+ DCHECK(CalledOnValidThread());
+ return type == kReadAccess || !read_only_;
+}
+
+bool RlzValueStoreChromeOS::WritePingTime(Product product, int64 time) {
+ DCHECK(CalledOnValidThread());
+ rlz_store_->SetString(GetKeyName(kPingTimeKey, product),
+ base::Int64ToString(time));
+ return true;
+}
+
+bool RlzValueStoreChromeOS::ReadPingTime(Product product, int64* time) {
+ DCHECK(CalledOnValidThread());
+ std::string ping_time;
+ return rlz_store_->GetString(GetKeyName(kPingTimeKey, product), &ping_time) &&
+ base::StringToInt64(ping_time, time);
+}
+
+bool RlzValueStoreChromeOS::ClearPingTime(Product product) {
+ DCHECK(CalledOnValidThread());
+ rlz_store_->Remove(GetKeyName(kPingTimeKey, product), NULL);
+ return true;
+}
+
+bool RlzValueStoreChromeOS::WriteAccessPointRlz(AccessPoint access_point,
+ const char* new_rlz) {
+ DCHECK(CalledOnValidThread());
+ rlz_store_->SetString(
+ GetKeyName(kAccessPointKey, access_point), new_rlz);
+ return true;
+}
+
+bool RlzValueStoreChromeOS::ReadAccessPointRlz(AccessPoint access_point,
+ char* rlz,
+ size_t rlz_size) {
+ DCHECK(CalledOnValidThread());
+ std::string rlz_value;
+ rlz_store_->GetString(GetKeyName(kAccessPointKey, access_point), &rlz_value);
+ if (rlz_value.size() < rlz_size) {
+ strncpy(rlz, rlz_value.c_str(), rlz_size);
+ return true;
+ }
+ if (rlz_size > 0)
+ *rlz = '\0';
+ return false;
+}
+
+bool RlzValueStoreChromeOS::ClearAccessPointRlz(AccessPoint access_point) {
+ DCHECK(CalledOnValidThread());
+ rlz_store_->Remove(GetKeyName(kAccessPointKey, access_point), NULL);
+ return true;
+}
+
+bool RlzValueStoreChromeOS::AddProductEvent(Product product,
+ const char* event_rlz) {
+ DCHECK(CalledOnValidThread());
+ return AddValueToList(GetKeyName(kProductEventKey, product),
+ base::Value::CreateStringValue(event_rlz));
+}
+
+bool RlzValueStoreChromeOS::ReadProductEvents(
+ Product product,
+ std::vector<std::string>* events) {
+ DCHECK(CalledOnValidThread());
+ base::ListValue* events_list = NULL; ;
+ if (!rlz_store_->GetList(GetKeyName(kProductEventKey, product), &events_list))
+ return false;
+ events->clear();
+ for (size_t i = 0; i < events_list->GetSize(); ++i) {
+ std::string event;
+ if (events_list->GetString(i, &event))
+ events->push_back(event);
+ }
+ return true;
+}
+
+bool RlzValueStoreChromeOS::ClearProductEvent(Product product,
+ const char* event_rlz) {
+ DCHECK(CalledOnValidThread());
+ base::StringValue event_value(event_rlz);
+ return RemoveValueFromList(GetKeyName(kProductEventKey, product),
+ event_value);
+}
+
+bool RlzValueStoreChromeOS::ClearAllProductEvents(Product product) {
+ DCHECK(CalledOnValidThread());
+ rlz_store_->Remove(GetKeyName(kProductEventKey, product), NULL);
+ return true;
+}
+
+bool RlzValueStoreChromeOS::AddStatefulEvent(Product product,
+ const char* event_rlz) {
+ DCHECK(CalledOnValidThread());
+ return AddValueToList(GetKeyName(kStatefulEventKey, product),
+ base::Value::CreateStringValue(event_rlz));
+}
+
+bool RlzValueStoreChromeOS::IsStatefulEvent(Product product,
+ const char* event_rlz) {
+ DCHECK(CalledOnValidThread());
+ base::StringValue event_value(event_rlz);
+ base::ListValue* events_list = NULL;
+ return rlz_store_->GetList(GetKeyName(kStatefulEventKey, product),
+ &events_list) &&
+ events_list->Find(event_value) != events_list->end();
+}
+
+bool RlzValueStoreChromeOS::ClearAllStatefulEvents(Product product) {
+ DCHECK(CalledOnValidThread());
+ rlz_store_->Remove(GetKeyName(kStatefulEventKey, product), NULL);
+ return true;
+}
+
+void RlzValueStoreChromeOS::CollectGarbage() {
+ DCHECK(CalledOnValidThread());
+ NOTIMPLEMENTED();
+}
+
+void RlzValueStoreChromeOS::ReadStore() {
+ int error_code = 0;
+ std::string error_msg;
+ JSONFileValueSerializer serializer(store_path_);
+ scoped_ptr<base::Value> value(
+ serializer.Deserialize(&error_code, &error_msg));
+ switch (error_code) {
+ case JSONFileValueSerializer::JSON_NO_SUCH_FILE:
+ read_only_ = false;
+ break;
+ case JSONFileValueSerializer::JSON_NO_ERROR:
+ read_only_ = false;
+ rlz_store_.reset(static_cast<base::DictionaryValue*>(value.release()));
+ break;
+ default:
+ LOG(ERROR) << "Error reading RLZ store: " << error_msg;
+ }
+}
+
+void RlzValueStoreChromeOS::WriteStore() {
+ std::string json_data;
+ JSONStringValueSerializer serializer(&json_data);
+ serializer.set_pretty_print(true);
+ scoped_ptr<DictionaryValue> copy(rlz_store_->DeepCopyWithoutEmptyChildren());
+ if (!serializer.Serialize(*copy.get())) {
+ LOG(ERROR) << "Failed to serialize RLZ data";
+ NOTREACHED();
+ return;
+ }
+ if (!base::ImportantFileWriter::WriteFileAtomically(store_path_, json_data))
+ LOG(ERROR) << "Error writing RLZ store";
+}
+
+bool RlzValueStoreChromeOS::AddValueToList(std::string list_name,
+ base::Value* value) {
+ base::ListValue* list_value = NULL;
+ if (!rlz_store_->GetList(list_name, &list_value)) {
+ list_value = new base::ListValue;
+ rlz_store_->Set(list_name, list_value);
+ }
+ list_value->AppendIfNotPresent(value);
+ return true;
+}
+
+bool RlzValueStoreChromeOS::RemoveValueFromList(std::string list_name,
+ const base::Value& value) {
+ base::ListValue* list_value = NULL;
+ if (!rlz_store_->GetList(list_name, &list_value))
+ return false;
+ size_t index;
+ list_value->Remove(value, &index);
+ return true;
+}
+
+namespace {
+
+// RlzValueStoreChromeOS keeps its data in memory and only writes it to disk
+// when ScopedRlzValueStoreLock goes out of scope. Hence, if several
+// ScopedRlzValueStoreLocks are nested, they all need to use the same store
+// object.
+
+RecursiveCrossProcessLock g_recursive_lock =
+ RECURSIVE_CROSS_PROCESS_LOCK_INITIALIZER;
+
+// This counts the nesting depth of |ScopedRlzValueStoreLock|.
+int g_lock_depth = 0;
+
+// This is the shared store object. Non-|NULL| only when |g_lock_depth > 0|.
+RlzValueStoreChromeOS* g_store = NULL;
+
+} // namespace
+
+ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() {
+ bool got_cross_process_lock =
+ g_recursive_lock.TryGetCrossProcessLock(GetRlzStoreLockPath());
+ // At this point, we hold the in-process lock, no matter the value of
+ // |got_cross_process_lock|.
+
+ ++g_lock_depth;
+ if (!got_cross_process_lock) {
+ // Acquiring cross-process lock failed, so simply return here.
+ // In-process lock will be released in dtor.
+ DCHECK(!g_store);
+ return;
+ }
+
+ if (g_lock_depth > 1) {
+ // Reuse the already existing store object.
+ DCHECK(g_store);
+ store_.reset(g_store);
+ return;
+ }
+
+ // This is the topmost lock, create a new store object.
+ DCHECK(!g_store);
+ g_store = new RlzValueStoreChromeOS(GetRlzStorePath());
+ store_.reset(g_store);
+}
+
+ScopedRlzValueStoreLock::~ScopedRlzValueStoreLock() {
+ --g_lock_depth;
+ DCHECK(g_lock_depth >= 0);
+
+ if (g_lock_depth > 0) {
+ // Other locks are still using store_, so don't free it yet.
+ ignore_result(store_.release());
+ return;
+ }
+
+ g_store = NULL;
+
+ g_recursive_lock.ReleaseLock();
+}
+
+RlzValueStore* ScopedRlzValueStoreLock::GetStore() {
+ return store_.get();
+}
+
+namespace testing {
+
+void SetRlzStoreDirectory(const base::FilePath& directory) {
+ g_testing_rlz_store_path_ = directory;
+}
+
+std::string RlzStoreFilenameStr() {
+ return GetRlzStorePath().value();
+}
+
+} // namespace testing
+
+} // namespace rlz_lib
diff --git a/chromium/rlz/chromeos/lib/rlz_value_store_chromeos.h b/chromium/rlz/chromeos/lib/rlz_value_store_chromeos.h
new file mode 100644
index 00000000000..29758a4c7d1
--- /dev/null
+++ b/chromium/rlz/chromeos/lib/rlz_value_store_chromeos.h
@@ -0,0 +1,86 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RLZ_CHROMEOS_LIB_RLZ_VALUE_STORE_CHROMEOS_H_
+#define RLZ_CHROMEOS_LIB_RLZ_VALUE_STORE_CHROMEOS_H_
+
+#include "base/files/file_path.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/values.h"
+#include "rlz/lib/rlz_value_store.h"
+
+namespace base {
+class ListValue;
+class SequencedTaskRunner;
+class Value;
+}
+
+namespace rlz_lib {
+
+// An implementation of RlzValueStore for ChromeOS.
+class RlzValueStoreChromeOS : public RlzValueStore,
+ public base::NonThreadSafe {
+ public:
+ // // Sets the task runner that will be used by ImportantFileWriter for write
+ // // operations.
+ // static void SetFileTaskRunner(base::SequencedTaskRunner* file_task_runner);
+
+ // Creates new instance and synchronously reads data from file.
+ RlzValueStoreChromeOS(const base::FilePath& store_path);
+ virtual ~RlzValueStoreChromeOS();
+
+ // RlzValueStore overrides:
+ virtual bool HasAccess(AccessType type) OVERRIDE;
+
+ virtual bool WritePingTime(Product product, int64 time) OVERRIDE;
+ virtual bool ReadPingTime(Product product, int64* time) OVERRIDE;
+ virtual bool ClearPingTime(Product product) OVERRIDE;
+
+ virtual bool WriteAccessPointRlz(AccessPoint access_point,
+ const char* new_rlz) OVERRIDE;
+ virtual bool ReadAccessPointRlz(AccessPoint access_point,
+ char* rlz,
+ size_t rlz_size) OVERRIDE;
+ virtual bool ClearAccessPointRlz(AccessPoint access_point) OVERRIDE;
+
+ virtual bool AddProductEvent(Product product, const char* event_rlz) OVERRIDE;
+ virtual bool ReadProductEvents(Product product,
+ std::vector<std::string>* events) OVERRIDE;
+ virtual bool ClearProductEvent(Product product,
+ const char* event_rlz) OVERRIDE;
+ virtual bool ClearAllProductEvents(Product product) OVERRIDE;
+
+ virtual bool AddStatefulEvent(Product product,
+ const char* event_rlz) OVERRIDE;
+ virtual bool IsStatefulEvent(Product product,
+ const char* event_rlz) OVERRIDE;
+ virtual bool ClearAllStatefulEvents(Product product) OVERRIDE;
+
+ virtual void CollectGarbage() OVERRIDE;
+
+ private:
+ // Reads RLZ store from file.
+ void ReadStore();
+
+ // Writes RLZ store back to file.
+ void WriteStore();
+
+ // Adds |value| to list at |list_name| path in JSON store.
+ bool AddValueToList(std::string list_name, base::Value* value);
+ // Removes |value| from list at |list_name| path in JSON store.
+ bool RemoveValueFromList(std::string list_name, const base::Value& value);
+
+ // In-memory store with RLZ data.
+ scoped_ptr<base::DictionaryValue> rlz_store_;
+
+ base::FilePath store_path_;
+
+ bool read_only_;
+
+ DISALLOW_COPY_AND_ASSIGN(RlzValueStoreChromeOS);
+};
+
+} // namespace rlz_lib
+
+#endif // RLZ_CHROMEOS_LIB_RLZ_VALUE_STORE_CHROMEOS_H_
diff --git a/chromium/rlz/examples/rlz_id.cc b/chromium/rlz/examples/rlz_id.cc
new file mode 100644
index 00000000000..ce01f1831f8
--- /dev/null
+++ b/chromium/rlz/examples/rlz_id.cc
@@ -0,0 +1,13 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "rlz/lib/machine_id.h"
+
+int main() {
+ std::string machine_id;
+ if (!rlz_lib::GetMachineId(&machine_id))
+ return 1;
+
+ printf("%s\n", machine_id.c_str());
+}
diff --git a/chromium/rlz/lib/assert.cc b/chromium/rlz/lib/assert.cc
new file mode 100644
index 00000000000..d8ad9358f63
--- /dev/null
+++ b/chromium/rlz/lib/assert.cc
@@ -0,0 +1,15 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Macros specific to the RLZ library.
+
+#include "rlz/lib/assert.h"
+
+namespace rlz_lib {
+
+#ifdef MUTE_EXPECTED_ASSERTS
+std::string expected_assertion_;
+#endif
+
+} // namespace rlz_lib
diff --git a/chromium/rlz/lib/assert.h b/chromium/rlz/lib/assert.h
new file mode 100644
index 00000000000..89e94e08d4b
--- /dev/null
+++ b/chromium/rlz/lib/assert.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Macros specific to the RLZ library.
+
+#ifndef RLZ_LIB_ASSERT_H_
+#define RLZ_LIB_ASSERT_H_
+
+#include <string>
+#include "base/logging.h"
+
+// An assertion macro.
+// Can mute expected assertions in debug mode.
+
+#ifndef ASSERT_STRING
+ #ifndef MUTE_EXPECTED_ASSERTS
+ #define ASSERT_STRING(expr) LOG_IF(FATAL, false) << (expr)
+ #else
+ #define ASSERT_STRING(expr) \
+ do { \
+ std::string expr_string(expr); \
+ if (rlz_lib::expected_assertion_ != expr_string) { \
+ LOG_IF(FATAL, false) << (expr); \
+ } \
+ } while (0)
+ #endif
+#endif
+
+
+#ifndef VERIFY
+ #ifdef _DEBUG
+ #define VERIFY(expr) LOG_IF(FATAL, !(expr)) << #expr
+ #else
+ #define VERIFY(expr) (void)(expr)
+ #endif
+#endif
+
+namespace rlz_lib {
+
+#ifdef MUTE_EXPECTED_ASSERTS
+extern std::string expected_assertion_;
+#endif
+
+inline void SetExpectedAssertion(const char* s) {
+#ifdef MUTE_EXPECTED_ASSERTS
+ expected_assertion_ = s;
+#endif
+}
+
+} // rlz_lib
+
+#endif // RLZ_LIB_ASSERT_H_
diff --git a/chromium/rlz/lib/crc32.h b/chromium/rlz/lib/crc32.h
new file mode 100644
index 00000000000..aded35588ee
--- /dev/null
+++ b/chromium/rlz/lib/crc32.h
@@ -0,0 +1,17 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A wrapper around ZLib's CRC function.
+
+#ifndef RLZ_LIB_CRC32_H_
+#define RLZ_LIB_CRC32_H_
+
+namespace rlz_lib {
+
+int Crc32(const unsigned char* buf, int length);
+bool Crc32(const char* text, int* crc);
+
+} // namespace rlz_lib
+
+#endif // RLZ_LIB_CRC32_H_
diff --git a/chromium/rlz/lib/crc32_unittest.cc b/chromium/rlz/lib/crc32_unittest.cc
new file mode 100644
index 00000000000..9700058754c
--- /dev/null
+++ b/chromium/rlz/lib/crc32_unittest.cc
@@ -0,0 +1,52 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A test for ZLib's checksum function.
+
+#include "rlz/lib/crc32.h"
+
+#include "base/logging.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(Crc32Unittest, ByteTest) {
+ struct {
+ const char* data;
+ int len;
+ // Externally calculated at http://crc32-checksum.waraxe.us/
+ int crc;
+ } kData[] = {
+ {"Hello" , 5, 0xF7D18982},
+ {"Google" , 6, 0x62B0F067},
+ {"" , 0, 0x0},
+ {"One more string.", 16, 0x0CA14970},
+ {NULL , 0, 0x0},
+ };
+
+ for (int i = 0; kData[i].data; i++)
+ EXPECT_EQ(kData[i].crc,
+ rlz_lib::Crc32(reinterpret_cast<const unsigned char*>(kData[i].data),
+ kData[i].len));
+}
+
+TEST(Crc32Unittest, CharTest) {
+ struct {
+ const char* data;
+ // Externally calculated at http://crc32-checksum.waraxe.us/
+ int crc;
+ } kData[] = {
+ {"Hello" , 0xF7D18982},
+ {"Google" , 0x62B0F067},
+ {"" , 0x0},
+ {"One more string.", 0x0CA14970},
+ {"Google\r\n" , 0x83A3E860},
+ {NULL , 0x0},
+ };
+
+ int crc;
+ for (int i = 0; kData[i].data; i++) {
+ EXPECT_TRUE(rlz_lib::Crc32(kData[i].data, &crc));
+ EXPECT_EQ(kData[i].crc, crc);
+ }
+}
diff --git a/chromium/rlz/lib/crc32_wrapper.cc b/chromium/rlz/lib/crc32_wrapper.cc
new file mode 100644
index 00000000000..d763c265c9f
--- /dev/null
+++ b/chromium/rlz/lib/crc32_wrapper.cc
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A wrapper around ZLib's CRC functions to put them in the rlz_lib namespace
+// and use our types.
+
+#include "rlz/lib/assert.h"
+#include "rlz/lib/crc32.h"
+#include "rlz/lib/string_utils.h"
+#include "third_party/zlib/zlib.h"
+
+namespace rlz_lib {
+
+int Crc32(const unsigned char* buf, int length) {
+ return crc32(0L, buf, length);
+}
+
+bool Crc32(const char* text, int* crc) {
+ if (!crc) {
+ ASSERT_STRING("Crc32: crc is NULL.");
+ return false;
+ }
+
+ *crc = 0;
+ for (int i = 0; text[i]; i++) {
+ if (!IsAscii(text[i]))
+ return false;
+
+ *crc = crc32(*crc, reinterpret_cast<const unsigned char*>(text + i), 1);
+ }
+
+ return true;
+}
+
+} // namespace rlz_lib
diff --git a/chromium/rlz/lib/crc8.cc b/chromium/rlz/lib/crc8.cc
new file mode 100644
index 00000000000..fe02eabf1b8
--- /dev/null
+++ b/chromium/rlz/lib/crc8.cc
@@ -0,0 +1,90 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "rlz/lib/crc8.h"
+
+namespace {
+
+// The CRC lookup table used for ATM HES (Polynomial = 0x07).
+// These are 256 unique 8-bit values.
+const unsigned char kCrcTable[256] = {
+ 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15,
+ 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
+ 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
+ 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
+ 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5,
+ 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
+ 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85,
+ 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
+ 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
+ 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
+ 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2,
+ 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
+ 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32,
+ 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
+ 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
+ 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
+ 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C,
+ 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
+ 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC,
+ 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
+ 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
+ 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
+ 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C,
+ 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
+ 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B,
+ 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
+ 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
+ 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
+ 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB,
+ 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
+ 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB,
+ 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
+};
+
+} // namespace anonymous
+
+
+namespace rlz_lib {
+
+bool Crc8::Generate(const unsigned char *data, int length,
+ unsigned char* check_sum) {
+ if (!check_sum)
+ return false;
+
+ *check_sum = 0;
+ if (!data)
+ return false;
+
+ // The inital and final constants are as used in the ATM HEC.
+ static const unsigned char kInitial = 0x00;
+ static const unsigned char kFinal = 0x55;
+ unsigned char crc = kInitial;
+ for (int i = 0; i < length; ++i) {
+ crc = kCrcTable[(data[i] ^ crc) & 0xFFU];
+ }
+
+ *check_sum = crc ^ kFinal;
+ return true;
+}
+
+bool Crc8::Verify(const unsigned char* data, int length,
+ unsigned char check_sum, bool* matches) {
+ if (!matches)
+ return false;
+
+ *matches = false;
+ if (!data)
+ return false;
+
+ unsigned char calculated_crc;
+ if (!Generate(data, length, &calculated_crc))
+ return false;
+
+ *matches = check_sum == calculated_crc;
+
+ return true;
+}
+
+} // namespace rlz_lib
diff --git a/chromium/rlz/lib/crc8.h b/chromium/rlz/lib/crc8.h
new file mode 100644
index 00000000000..6c3c84859bf
--- /dev/null
+++ b/chromium/rlz/lib/crc8.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Crc8 utility functions.
+
+#ifndef RLZ_LIB_CRC8_H_
+#define RLZ_LIB_CRC8_H_
+
+namespace rlz_lib {
+// CRC-8 methods:
+class Crc8 {
+ public:
+ static bool Generate(const unsigned char* data,
+ int length,
+ unsigned char* check_sum);
+ static bool Verify(const unsigned char* data,
+ int length,
+ unsigned char checksum,
+ bool * matches);
+};
+}; // namespace rlz_lib
+
+#endif // RLZ_LIB_CRC8_H_
diff --git a/chromium/rlz/lib/crc8_unittest.cc b/chromium/rlz/lib/crc8_unittest.cc
new file mode 100644
index 00000000000..48892429d9d
--- /dev/null
+++ b/chromium/rlz/lib/crc8_unittest.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Uniitest for data encryption functions.
+
+#include "base/logging.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "rlz/lib/crc8.h"
+
+TEST(Crc8Unittest, TestCrc8) {
+ struct Data {
+ char string[10];
+ // Externally calculated checksums use
+ // http://www.zorc.breitbandkatze.de/crc.html
+ // with the ATM HEC paramters:
+ // CRC-8, Polynomial 0x07, Initial value 0x00, Final XOR value 0x55
+ // (direct, don't reverse data byes, don't reverse CRC before final XOR)
+ unsigned char external_crc;
+ int random_byte;
+ unsigned char corrupt_value;
+ } data[] = {
+ {"Google", 0x01, 2, 0x53},
+ {"GOOGLE", 0xA6, 4, 0x11},
+ {"My CRC 8!", 0xDC, 0, 0x50},
+ };
+
+ unsigned char* bytes;
+ unsigned char crc;
+ bool matches;
+ int length;
+ for (size_t i = 0; i < sizeof(data) / sizeof(data[0]); ++i) {
+ bytes = reinterpret_cast<unsigned char*>(data[i].string);
+ crc = 0;
+ matches = false;
+ length = strlen(data[i].string);
+
+ // Calculate CRC and compare against external value.
+ rlz_lib::Crc8::Generate(bytes, length, &crc);
+ EXPECT_TRUE(crc == data[i].external_crc);
+ rlz_lib::Crc8::Verify(bytes, length, crc, &matches);
+ EXPECT_TRUE(matches);
+
+ // Corrupt string and see if CRC still matches.
+ data[i].string[data[i].random_byte] = data[i].corrupt_value;
+ rlz_lib::Crc8::Verify(bytes, length, crc, &matches);
+ EXPECT_FALSE(matches);
+ }
+}
diff --git a/chromium/rlz/lib/financial_ping.cc b/chromium/rlz/lib/financial_ping.cc
new file mode 100644
index 00000000000..5f152569dd6
--- /dev/null
+++ b/chromium/rlz/lib/financial_ping.cc
@@ -0,0 +1,362 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Library functions related to the Financial Server ping.
+
+#include "rlz/lib/financial_ping.h"
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "rlz/lib/assert.h"
+#include "rlz/lib/lib_values.h"
+#include "rlz/lib/machine_id.h"
+#include "rlz/lib/rlz_lib.h"
+#include "rlz/lib/rlz_value_store.h"
+#include "rlz/lib/string_utils.h"
+
+#if !defined(OS_WIN)
+#include "base/time/time.h"
+#endif
+
+#if defined(RLZ_NETWORK_IMPLEMENTATION_WIN_INET)
+
+#include <windows.h>
+#include <wininet.h>
+
+namespace {
+
+class InternetHandle {
+ public:
+ InternetHandle(HINTERNET handle) { handle_ = handle; }
+ ~InternetHandle() { if (handle_) InternetCloseHandle(handle_); }
+ operator HINTERNET() const { return handle_; }
+ bool operator!() const { return (handle_ == NULL); }
+
+ private:
+ HINTERNET handle_;
+};
+
+} // namespace
+
+#else
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/time/time.h"
+#include "net/base/load_flags.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "url/gurl.h"
+
+#endif
+
+namespace {
+
+// Returns the time relative to a fixed point in the past in multiples of
+// 100 ns stepts. The point in the past is arbitrary but can't change, as the
+// result of this value is stored on disk.
+int64 GetSystemTimeAsInt64() {
+#if defined(OS_WIN)
+ FILETIME now_as_file_time;
+ // Relative to Jan 1, 1601 (UTC).
+ GetSystemTimeAsFileTime(&now_as_file_time);
+
+ LARGE_INTEGER integer;
+ integer.HighPart = now_as_file_time.dwHighDateTime;
+ integer.LowPart = now_as_file_time.dwLowDateTime;
+ return integer.QuadPart;
+#else
+ // Seconds since epoch (Jan 1, 1970).
+ double now_seconds = base::Time::Now().ToDoubleT();
+ return static_cast<int64>(now_seconds * 1000 * 1000 * 10);
+#endif
+}
+
+} // namespace
+
+
+namespace rlz_lib {
+
+bool FinancialPing::FormRequest(Product product,
+ const AccessPoint* access_points, const char* product_signature,
+ const char* product_brand, const char* product_id,
+ const char* product_lang, bool exclude_machine_id,
+ std::string* request) {
+ if (!request) {
+ ASSERT_STRING("FinancialPing::FormRequest: request is NULL");
+ return false;
+ }
+
+ request->clear();
+
+ ScopedRlzValueStoreLock lock;
+ RlzValueStore* store = lock.GetStore();
+ if (!store || !store->HasAccess(RlzValueStore::kReadAccess))
+ return false;
+
+ if (!access_points) {
+ ASSERT_STRING("FinancialPing::FormRequest: access_points is NULL");
+ return false;
+ }
+
+ if (!product_signature) {
+ ASSERT_STRING("FinancialPing::FormRequest: product_signature is NULL");
+ return false;
+ }
+
+ if (!SupplementaryBranding::GetBrand().empty()) {
+ if (SupplementaryBranding::GetBrand() != product_brand) {
+ ASSERT_STRING("FinancialPing::FormRequest: supplementary branding bad");
+ return false;
+ }
+ }
+
+ base::StringAppendF(request, "%s?", kFinancialPingPath);
+
+ // Add the signature, brand, product id and language.
+ base::StringAppendF(request, "%s=%s", kProductSignatureCgiVariable,
+ product_signature);
+ if (product_brand)
+ base::StringAppendF(request, "&%s=%s", kProductBrandCgiVariable,
+ product_brand);
+
+ if (product_id)
+ base::StringAppendF(request, "&%s=%s", kProductIdCgiVariable, product_id);
+
+ if (product_lang)
+ base::StringAppendF(request, "&%s=%s", kProductLanguageCgiVariable,
+ product_lang);
+
+ // Add the product events.
+ char cgi[kMaxCgiLength + 1];
+ cgi[0] = 0;
+ bool has_events = GetProductEventsAsCgi(product, cgi, arraysize(cgi));
+ if (has_events)
+ base::StringAppendF(request, "&%s", cgi);
+
+ // If we don't have any events, we should ping all the AP's on the system
+ // that we know about and have a current RLZ value, even if they are not
+ // used by this product.
+ AccessPoint all_points[LAST_ACCESS_POINT];
+ if (!has_events) {
+ char rlz[kMaxRlzLength + 1];
+ int idx = 0;
+ for (int ap = NO_ACCESS_POINT + 1; ap < LAST_ACCESS_POINT; ap++) {
+ rlz[0] = 0;
+ AccessPoint point = static_cast<AccessPoint>(ap);
+ if (GetAccessPointRlz(point, rlz, arraysize(rlz)) &&
+ rlz[0] != '\0')
+ all_points[idx++] = point;
+ }
+ all_points[idx] = NO_ACCESS_POINT;
+ }
+
+ // Add the RLZ's and the DCC if needed. This is the same as get PingParams.
+ // This will also include the RLZ Exchange Protocol CGI Argument.
+ cgi[0] = 0;
+ if (GetPingParams(product, has_events ? access_points : all_points,
+ cgi, arraysize(cgi)))
+ base::StringAppendF(request, "&%s", cgi);
+
+ if (has_events && !exclude_machine_id) {
+ std::string machine_id;
+ if (GetMachineId(&machine_id)) {
+ base::StringAppendF(request, "&%s=%s", kMachineIdCgiVariable,
+ machine_id.c_str());
+ }
+ }
+
+ return true;
+}
+
+#if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
+// The URLRequestContextGetter used by FinancialPing::PingServer().
+net::URLRequestContextGetter* g_context;
+
+bool FinancialPing::SetURLRequestContext(
+ net::URLRequestContextGetter* context) {
+ g_context = context;
+ return true;
+}
+
+namespace {
+
+class FinancialPingUrlFetcherDelegate : public net::URLFetcherDelegate {
+ public:
+ FinancialPingUrlFetcherDelegate(const base::Closure& callback)
+ : callback_(callback) {
+ }
+ virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
+
+ private:
+ base::Closure callback_;
+};
+
+void FinancialPingUrlFetcherDelegate::OnURLFetchComplete(
+ const net::URLFetcher* source) {
+ callback_.Run();
+}
+
+} // namespace
+
+#endif
+
+bool FinancialPing::PingServer(const char* request, std::string* response) {
+ if (!response)
+ return false;
+
+ response->clear();
+
+#if defined(RLZ_NETWORK_IMPLEMENTATION_WIN_INET)
+ // Initialize WinInet.
+ InternetHandle inet_handle = InternetOpenA(kFinancialPingUserAgent,
+ INTERNET_OPEN_TYPE_PRECONFIG,
+ NULL, NULL, 0);
+ if (!inet_handle)
+ return false;
+
+ // Open network connection.
+ InternetHandle connection_handle = InternetConnectA(inet_handle,
+ kFinancialServer, kFinancialPort, "", "", INTERNET_SERVICE_HTTP,
+ INTERNET_FLAG_NO_CACHE_WRITE, 0);
+ if (!connection_handle)
+ return false;
+
+ // Prepare the HTTP request.
+ InternetHandle http_handle = HttpOpenRequestA(connection_handle,
+ "GET", request, NULL, NULL, kFinancialPingResponseObjects,
+ INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES, NULL);
+ if (!http_handle)
+ return false;
+
+ // Timeouts are probably:
+ // INTERNET_OPTION_SEND_TIMEOUT, INTERNET_OPTION_RECEIVE_TIMEOUT
+
+ // Send the HTTP request. Note: Fails if user is working in off-line mode.
+ if (!HttpSendRequest(http_handle, NULL, 0, NULL, 0))
+ return false;
+
+ // Check the response status.
+ DWORD status;
+ DWORD status_size = sizeof(status);
+ if (!HttpQueryInfo(http_handle, HTTP_QUERY_STATUS_CODE |
+ HTTP_QUERY_FLAG_NUMBER, &status, &status_size, NULL) ||
+ 200 != status)
+ return false;
+
+ // Get the response text.
+ scoped_ptr<char[]> buffer(new char[kMaxPingResponseLength]);
+ if (buffer.get() == NULL)
+ return false;
+
+ DWORD bytes_read = 0;
+ while (InternetReadFile(http_handle, buffer.get(), kMaxPingResponseLength,
+ &bytes_read) && bytes_read > 0) {
+ response->append(buffer.get(), bytes_read);
+ bytes_read = 0;
+ };
+
+ return true;
+#else
+ // Browser shutdown will cause the context to be reset to NULL.
+ if (!g_context)
+ return false;
+
+ // Run a blocking event loop to match the win inet implementation.
+ scoped_ptr<base::MessageLoop> message_loop;
+ // Ensure that we have a MessageLoop.
+ if (!base::MessageLoop::current())
+ message_loop.reset(new base::MessageLoop);
+ base::RunLoop loop;
+ FinancialPingUrlFetcherDelegate delegate(loop.QuitClosure());
+
+ std::string url = base::StringPrintf("http://%s:%d%s",
+ kFinancialServer, kFinancialPort,
+ request);
+
+ scoped_ptr<net::URLFetcher> fetcher(net::URLFetcher::Create(
+ GURL(url), net::URLFetcher::GET, &delegate));
+
+ fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE |
+ net::LOAD_DO_NOT_SEND_AUTH_DATA |
+ net::LOAD_DO_NOT_PROMPT_FOR_LOGIN |
+ net::LOAD_DO_NOT_SEND_COOKIES |
+ net::LOAD_DO_NOT_SAVE_COOKIES);
+
+ // Ensure rlz_lib::SetURLRequestContext() has been called before sending
+ // pings.
+ fetcher->SetRequestContext(g_context);
+
+ const base::TimeDelta kTimeout = base::TimeDelta::FromMinutes(5);
+ base::MessageLoop::ScopedNestableTaskAllower allow_nested(
+ base::MessageLoop::current());
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&net::URLFetcher::Start, base::Unretained(fetcher.get())));
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, loop.QuitClosure(), kTimeout);
+
+ loop.Run();
+
+ if (fetcher->GetResponseCode() != 200)
+ return false;
+
+ return fetcher->GetResponseAsString(response);
+#endif
+}
+
+bool FinancialPing::IsPingTime(Product product, bool no_delay) {
+ ScopedRlzValueStoreLock lock;
+ RlzValueStore* store = lock.GetStore();
+ if (!store || !store->HasAccess(RlzValueStore::kReadAccess))
+ return false;
+
+ int64 last_ping = 0;
+ if (!store->ReadPingTime(product, &last_ping))
+ return true;
+
+ uint64 now = GetSystemTimeAsInt64();
+ int64 interval = now - last_ping;
+
+ // If interval is negative, clock was probably reset. So ping.
+ if (interval < 0)
+ return true;
+
+ // Check if this product has any unreported events.
+ char cgi[kMaxCgiLength + 1];
+ cgi[0] = 0;
+ bool has_events = GetProductEventsAsCgi(product, cgi, arraysize(cgi));
+ if (no_delay && has_events)
+ return true;
+
+ return interval >= (has_events ? kEventsPingInterval : kNoEventsPingInterval);
+}
+
+
+bool FinancialPing::UpdateLastPingTime(Product product) {
+ ScopedRlzValueStoreLock lock;
+ RlzValueStore* store = lock.GetStore();
+ if (!store || !store->HasAccess(RlzValueStore::kWriteAccess))
+ return false;
+
+ uint64 now = GetSystemTimeAsInt64();
+ return store->WritePingTime(product, now);
+}
+
+
+bool FinancialPing::ClearLastPingTime(Product product) {
+ ScopedRlzValueStoreLock lock;
+ RlzValueStore* store = lock.GetStore();
+ if (!store || !store->HasAccess(RlzValueStore::kWriteAccess))
+ return false;
+ return store->ClearPingTime(product);
+}
+
+} // namespace
diff --git a/chromium/rlz/lib/financial_ping.h b/chromium/rlz/lib/financial_ping.h
new file mode 100644
index 00000000000..081ecbac3d9
--- /dev/null
+++ b/chromium/rlz/lib/financial_ping.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Library functions related to the Financial Server ping.
+
+#ifndef RLZ_LIB_FINANCIAL_PING_H_
+#define RLZ_LIB_FINANCIAL_PING_H_
+
+#include <string>
+#include "rlz/lib/rlz_enums.h"
+
+#if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
+namespace net {
+class URLRequestContextGetter;
+} // namespace net
+#endif
+
+namespace rlz_lib {
+
+class FinancialPing {
+ public:
+ // Form the HTTP request to send to the PSO server.
+ // Will look something like:
+ // /pso/ping?as=swg&brand=GGLD&id=124&hl=en&
+ // events=I7S&rep=1&rlz=I7:val,W1:&dcc=dval
+ static bool FormRequest(Product product, const AccessPoint* access_points,
+ const char* product_signature,
+ const char* product_brand, const char* product_id,
+ const char* product_lang, bool exclude_machine_id,
+ std::string* request);
+
+ // Returns whether the time is right to send a ping.
+ // If no_delay is true, this should always ping if there are events,
+ // or one week has passed since last_ping when there are no new events.
+ // If no_delay is false, this should ping if current time < last_ping time
+ // (case of time reset) or if one day has passed since last_ping and there
+ // are events, or one week has passed since last_ping when there are
+ // no new events.
+ static bool IsPingTime(Product product, bool no_delay);
+
+ // Set the last ping time to be now. Writes to RlzValueStore.
+ static bool UpdateLastPingTime(Product product);
+
+ // Clear the last ping time - should be called on uninstall.
+ // Writes to RlzValueStore.
+ static bool ClearLastPingTime(Product product);
+
+ // Ping the financial server with request. Writes to RlzValueStore.
+ static bool PingServer(const char* request, std::string* response);
+
+#if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
+ static bool SetURLRequestContext(net::URLRequestContextGetter* context);
+#endif
+
+ private:
+ FinancialPing() {}
+ ~FinancialPing() {}
+};
+
+} // namespace rlz_lib
+
+
+#endif // RLZ_LIB_FINANCIAL_PING_H_
diff --git a/chromium/rlz/lib/financial_ping_test.cc b/chromium/rlz/lib/financial_ping_test.cc
new file mode 100644
index 00000000000..4580a1f061f
--- /dev/null
+++ b/chromium/rlz/lib/financial_ping_test.cc
@@ -0,0 +1,293 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A test application for the FinancialPing class.
+//
+// These tests should not be executed on the build server:
+// - They modify machine state (registry).
+//
+// These tests require write access to HKCU and HKLM.
+//
+// The "GGLA" brand is used to test the normal code flow of the code, and the
+// "TEST" brand is used to test the supplementary brand code code flow. In one
+// case below, the brand "GOOG" is used because the code wants to use a brand
+// that is neither of the two mentioned above.
+
+#include "rlz/lib/financial_ping.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "rlz/lib/lib_values.h"
+#include "rlz/lib/machine_id.h"
+#include "rlz/lib/rlz_lib.h"
+#include "rlz/lib/rlz_value_store.h"
+#include "rlz/test/rlz_test_helpers.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#include "rlz/win/lib/machine_deal.h"
+#else
+#include "base/time/time.h"
+#endif
+
+namespace {
+
+// Must match the implementation in file_time.cc.
+int64 GetSystemTimeAsInt64() {
+#if defined(OS_WIN)
+ FILETIME now_as_file_time;
+ GetSystemTimeAsFileTime(&now_as_file_time);
+ LARGE_INTEGER integer;
+ integer.HighPart = now_as_file_time.dwHighDateTime;
+ integer.LowPart = now_as_file_time.dwLowDateTime;
+ return integer.QuadPart;
+#else
+ double now_seconds = base::Time::Now().ToDoubleT();
+ return static_cast<int64>(now_seconds * 1000 * 1000 * 10);
+#endif
+}
+
+// Ping times in 100-nanosecond intervals.
+const int64 k1MinuteInterval = 60LL * 10000000LL; // 1 minute
+
+} // namespace anonymous
+
+class FinancialPingTest : public RlzLibTestBase {
+};
+
+TEST_F(FinancialPingTest, FormRequest) {
+ std::string brand_string = rlz_lib::SupplementaryBranding::GetBrand();
+ const char* brand = brand_string.empty() ? "GGLA" : brand_string.c_str();
+
+#if defined(OS_WIN)
+ EXPECT_TRUE(rlz_lib::MachineDealCode::Set("dcc_value"));
+#define DCC_PARAM "&dcc=dcc_value"
+#else
+#define DCC_PARAM ""
+#endif
+
+ EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX,
+ "TbRlzValue"));
+
+ EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
+
+ rlz_lib::AccessPoint points[] =
+ {rlz_lib::IETB_SEARCH_BOX, rlz_lib::NO_ACCESS_POINT,
+ rlz_lib::NO_ACCESS_POINT};
+
+ std::string machine_id;
+ bool got_machine_id = rlz_lib::GetMachineId(&machine_id);
+
+ std::string request;
+ EXPECT_TRUE(rlz_lib::FinancialPing::FormRequest(rlz_lib::TOOLBAR_NOTIFIER,
+ points, "swg", brand, NULL, "en", false, &request));
+ std::string expected_response;
+ base::StringAppendF(&expected_response,
+ "/tools/pso/ping?as=swg&brand=%s&hl=en&"
+ "events=I7S,W1I&rep=2&rlz=T4:TbRlzValue" DCC_PARAM
+, brand);
+
+ if (got_machine_id)
+ base::StringAppendF(&expected_response, "&id=%s", machine_id.c_str());
+ EXPECT_EQ(expected_response, request);
+
+ EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, ""));
+ EXPECT_TRUE(rlz_lib::FinancialPing::FormRequest(rlz_lib::TOOLBAR_NOTIFIER,
+ points, "swg", brand, "IdOk2", NULL, false, &request));
+ expected_response.clear();
+ base::StringAppendF(&expected_response,
+ "/tools/pso/ping?as=swg&brand=%s&pid=IdOk2&"
+ "events=I7S,W1I&rep=2&rlz=T4:" DCC_PARAM, brand);
+
+ if (got_machine_id)
+ base::StringAppendF(&expected_response, "&id=%s", machine_id.c_str());
+ EXPECT_EQ(expected_response, request);
+
+ EXPECT_TRUE(rlz_lib::FinancialPing::FormRequest(rlz_lib::TOOLBAR_NOTIFIER,
+ points, "swg", brand, "IdOk", NULL, true, &request));
+ expected_response.clear();
+ base::StringAppendF(&expected_response,
+ "/tools/pso/ping?as=swg&brand=%s&pid=IdOk&"
+ "events=I7S,W1I&rep=2&rlz=T4:" DCC_PARAM, brand);
+ EXPECT_EQ(expected_response, request);
+
+ EXPECT_TRUE(rlz_lib::FinancialPing::FormRequest(rlz_lib::TOOLBAR_NOTIFIER,
+ points, "swg", brand, NULL, NULL, true, &request));
+ expected_response.clear();
+ base::StringAppendF(&expected_response,
+ "/tools/pso/ping?as=swg&brand=%s&events=I7S,W1I&rep=2"
+ "&rlz=T4:" DCC_PARAM, brand);
+ EXPECT_EQ(expected_response, request);
+
+
+ // Clear all events.
+ EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
+
+ // Clear all RLZs.
+ char rlz[rlz_lib::kMaxRlzLength + 1];
+ for (int ap = rlz_lib::NO_ACCESS_POINT + 1;
+ ap < rlz_lib::LAST_ACCESS_POINT; ap++) {
+ rlz[0] = 0;
+ rlz_lib::AccessPoint point = static_cast<rlz_lib::AccessPoint>(ap);
+ if (rlz_lib::GetAccessPointRlz(point, rlz, arraysize(rlz)) && rlz[0]) {
+ rlz_lib::SetAccessPointRlz(point, "");
+ }
+ }
+
+ EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX,
+ "TbRlzValue"));
+ EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::QUICK_SEARCH_BOX,
+ "QsbRlzValue"));
+ EXPECT_TRUE(rlz_lib::FinancialPing::FormRequest(rlz_lib::TOOLBAR_NOTIFIER,
+ points, "swg", brand, NULL, NULL, false, &request));
+ expected_response.clear();
+ base::StringAppendF(&expected_response,
+ "/tools/pso/ping?as=swg&brand=%s&rep=2&rlz=T4:TbRlzValue,"
+ "Q1:QsbRlzValue" DCC_PARAM, brand);
+ EXPECT_STREQ(expected_response.c_str(), request.c_str());
+
+ if (!GetAccessPointRlz(rlz_lib::IE_HOME_PAGE, rlz, arraysize(rlz))) {
+ points[2] = rlz_lib::IE_HOME_PAGE;
+ EXPECT_TRUE(rlz_lib::FinancialPing::FormRequest(rlz_lib::TOOLBAR_NOTIFIER,
+ points, "swg", brand, "MyId", "en-US", true, &request));
+ expected_response.clear();
+ base::StringAppendF(&expected_response,
+ "/tools/pso/ping?as=swg&brand=%s&hl=en-US&pid=MyId&rep=2"
+ "&rlz=T4:TbRlzValue,Q1:QsbRlzValue" DCC_PARAM, brand);
+ EXPECT_STREQ(expected_response.c_str(), request.c_str());
+ }
+}
+
+TEST_F(FinancialPingTest, FormRequestBadBrand) {
+ rlz_lib::AccessPoint points[] =
+ {rlz_lib::IETB_SEARCH_BOX, rlz_lib::NO_ACCESS_POINT,
+ rlz_lib::NO_ACCESS_POINT};
+
+ std::string request;
+ bool ok = rlz_lib::FinancialPing::FormRequest(rlz_lib::TOOLBAR_NOTIFIER,
+ points, "swg", "GOOG", NULL, "en", false, &request);
+ EXPECT_EQ(rlz_lib::SupplementaryBranding::GetBrand().empty(), ok);
+}
+
+
+static void SetLastPingTime(int64 time, rlz_lib::Product product) {
+ rlz_lib::ScopedRlzValueStoreLock lock;
+ rlz_lib::RlzValueStore* store = lock.GetStore();
+ ASSERT_TRUE(store);
+ ASSERT_TRUE(store->HasAccess(rlz_lib::RlzValueStore::kWriteAccess));
+ store->WritePingTime(product, time);
+}
+
+TEST_F(FinancialPingTest, IsPingTime) {
+ int64 now = GetSystemTimeAsInt64();
+ int64 last_ping = now - rlz_lib::kEventsPingInterval - k1MinuteInterval;
+ SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER);
+
+ // No events, last ping just over a day ago.
+ EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
+ EXPECT_FALSE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
+ false));
+
+ // Has events, last ping just over a day ago.
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+ EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
+ false));
+
+ // Has events, last ping just under a day ago.
+ last_ping = now - rlz_lib::kEventsPingInterval + k1MinuteInterval;
+ SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER);
+ EXPECT_FALSE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
+ false));
+
+ EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
+
+ // No events, last ping just under a week ago.
+ last_ping = now - rlz_lib::kNoEventsPingInterval + k1MinuteInterval;
+ SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER);
+ EXPECT_FALSE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
+ false));
+
+ // No events, last ping just over a week ago.
+ last_ping = now - rlz_lib::kNoEventsPingInterval - k1MinuteInterval;
+ SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER);
+ EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
+ false));
+
+ // Last ping was in future (invalid).
+ last_ping = now + k1MinuteInterval;
+ SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER);
+ EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
+ false));
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+ EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
+ false));
+}
+
+TEST_F(FinancialPingTest, BrandingIsPingTime) {
+ // Don't run these tests if a supplementary brand is already in place. That
+ // way we can control the branding.
+ if (!rlz_lib::SupplementaryBranding::GetBrand().empty())
+ return;
+
+ int64 now = GetSystemTimeAsInt64();
+ int64 last_ping = now - rlz_lib::kEventsPingInterval - k1MinuteInterval;
+ SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER);
+
+ // Has events, last ping just over a day ago.
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+ EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
+ false));
+
+ {
+ rlz_lib::SupplementaryBranding branding("TEST");
+ SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER);
+
+ // Has events, last ping just over a day ago.
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+ EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
+ false));
+ }
+
+ last_ping = now - k1MinuteInterval;
+ SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER);
+
+ EXPECT_FALSE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
+ false));
+
+ {
+ rlz_lib::SupplementaryBranding branding("TEST");
+ EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
+ false));
+ }
+}
+
+TEST_F(FinancialPingTest, ClearLastPingTime) {
+ int64 now = GetSystemTimeAsInt64();
+ int64 last_ping = now - rlz_lib::kEventsPingInterval + k1MinuteInterval;
+ SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER);
+
+ // Has events, last ping just under a day ago.
+ EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+ EXPECT_FALSE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
+ false));
+
+ EXPECT_TRUE(rlz_lib::FinancialPing::ClearLastPingTime(
+ rlz_lib::TOOLBAR_NOTIFIER));
+ EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
+ false));
+}
diff --git a/chromium/rlz/lib/lib_values.cc b/chromium/rlz/lib/lib_values.cc
new file mode 100644
index 00000000000..c4ef460c685
--- /dev/null
+++ b/chromium/rlz/lib/lib_values.cc
@@ -0,0 +1,208 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Key and value names of the location of the RLZ shared state.
+
+#include "rlz/lib/lib_values.h"
+
+#include "base/strings/stringprintf.h"
+#include "rlz/lib/assert.h"
+
+namespace rlz_lib {
+
+//
+// Ping information.
+//
+
+// rep=2: includes the new stateful events.
+const char kProtocolCgiArgument[] = "rep=2";
+
+const char kEventsCgiVariable[] = "events";
+const char kStatefulEventsCgiVariable[] = "stateful-events";
+const char kEventsCgiSeparator = ',';
+
+const char kRlzCgiVariable[] = "rlz";
+const char kRlzCgiSeparator[] = ",";
+const char kRlzCgiIndicator[] = ":";
+
+const char kProductSignatureCgiVariable[] = "as";
+const char kProductBrandCgiVariable[] = "brand";
+const char kProductLanguageCgiVariable[] = "hl";
+const char kProductIdCgiVariable[] = "pid";
+
+const char kDccCgiVariable[] = "dcc";
+const char kRlsCgiVariable[] = "rls";
+const char kMachineIdCgiVariable[] = "id";
+const char kSetDccResponseVariable[] = "set_dcc";
+
+//
+// Financial server information.
+//
+
+const char kFinancialPingPath[] = "/tools/pso/ping";
+const char kFinancialServer[] = "clients1.google.com";
+const int kFinancialPort = 80;
+
+// Ping times in 100-nanosecond intervals.
+const int64 kEventsPingInterval = 24LL * 3600LL * 10000000LL; // 1 day
+const int64 kNoEventsPingInterval = kEventsPingInterval * 7LL; // 1 week
+
+const char kFinancialPingUserAgent[] = "Mozilla/4.0 (compatible; Win32)";
+const char* kFinancialPingResponseObjects[] = { "text/*", NULL };
+
+//
+// AccessPoint and Event names.
+//
+//
+
+const char* GetAccessPointName(AccessPoint point) {
+ switch (point) {
+ case NO_ACCESS_POINT: return "";
+ case IE_DEFAULT_SEARCH: return "I7";
+ case IE_HOME_PAGE: return "W1";
+ case IETB_SEARCH_BOX: return "T4";
+ case QUICK_SEARCH_BOX: return "Q1";
+ case GD_DESKBAND: return "D1";
+ case GD_SEARCH_GADGET: return "D2";
+ case GD_WEB_SERVER: return "D3";
+ case GD_OUTLOOK: return "D4";
+ case CHROME_OMNIBOX: return "C1";
+ case CHROME_HOME_PAGE: return "C2";
+ case FFTB2_BOX: return "B2";
+ case FFTB3_BOX: return "B3";
+ case PINYIN_IME_BHO: return "N1";
+ case IGOOGLE_WEBPAGE: return "G1";
+ case MOBILE_IDLE_SCREEN_BLACKBERRY: return "H1";
+ case MOBILE_IDLE_SCREEN_WINMOB: return "H2";
+ case MOBILE_IDLE_SCREEN_SYMBIAN: return "H3";
+ case FF_HOME_PAGE: return "R0";
+ case FF_SEARCH_BOX: return "R1";
+ case IE_BROWSED_PAGE: return "R2";
+ case QSB_WIN_BOX: return "R3";
+ case WEBAPPS_CALENDAR: return "R4";
+ case WEBAPPS_DOCS: return "R5";
+ case WEBAPPS_GMAIL: return "R6";
+ case IETB_LINKDOCTOR: return "R7";
+ case FFTB_LINKDOCTOR: return "R8";
+ case IETB7_SEARCH_BOX: return "T7";
+ case TB8_SEARCH_BOX: return "T8";
+ case CHROME_FRAME: return "C3";
+ case PARTNER_AP_1: return "V1";
+ case PARTNER_AP_2: return "V2";
+ case PARTNER_AP_3: return "V3";
+ case PARTNER_AP_4: return "V4";
+ case PARTNER_AP_5: return "V5";
+ case CHROME_MAC_OMNIBOX: return "C5";
+ case CHROME_MAC_HOME_PAGE: return "C6";
+ case CHROMEOS_OMNIBOX: return "CA";
+ case CHROMEOS_HOME_PAGE: return "CB";
+ case CHROMEOS_RESERVED: return "CC";
+ case UNDEFINED_AP_M: return "RM";
+ case UNDEFINED_AP_N: return "RN";
+ case UNDEFINED_AP_O: return "RO";
+ case UNDEFINED_AP_P: return "RP";
+ case UNDEFINED_AP_Q: return "RQ";
+ case UNDEFINED_AP_R: return "RR";
+ case UNDEFINED_AP_S: return "RS";
+ case UNDEFINED_AP_T: return "RT";
+ case UNDEFINED_AP_U: return "RU";
+ case UNDEFINED_AP_V: return "RV";
+ case UNDEFINED_AP_W: return "RW";
+ case UNDEFINED_AP_X: return "RX";
+ case UNDEFINED_AP_Y: return "RY";
+ case UNDEFINED_AP_Z: return "RZ";
+ case PACK_AP0: return "U0";
+ case PACK_AP1: return "U1";
+ case PACK_AP2: return "U2";
+ case PACK_AP3: return "U3";
+ case PACK_AP4: return "U4";
+ case PACK_AP5: return "U5";
+ case PACK_AP6: return "U6";
+ case PACK_AP7: return "U7";
+ case PACK_AP8: return "U8";
+ case PACK_AP9: return "U9";
+ case PACK_AP10: return "UA";
+ case PACK_AP11: return "UB";
+ case PACK_AP12: return "UC";
+ case PACK_AP13: return "UD";
+ case LAST_ACCESS_POINT: ; // Fall through.
+ }
+
+ ASSERT_STRING("GetAccessPointName: Unknown Access Point");
+ return NULL;
+}
+
+
+bool GetAccessPointFromName(const char* name, AccessPoint* point) {
+ if (!point) {
+ ASSERT_STRING("GetAccessPointFromName: point is NULL");
+ return false;
+ }
+ *point = NO_ACCESS_POINT;
+ if (!name)
+ return false;
+
+ for (int i = NO_ACCESS_POINT; i < LAST_ACCESS_POINT; i++)
+ if (strcmp(name, GetAccessPointName(static_cast<AccessPoint>(i))) == 0) {
+ *point = static_cast<AccessPoint>(i);
+ return true;
+ }
+
+ return false;
+}
+
+
+const char* GetEventName(Event event) {
+ switch (event) {
+ case INVALID_EVENT: return "";
+ case INSTALL: return "I";
+ case SET_TO_GOOGLE: return "S";
+ case FIRST_SEARCH: return "F";
+ case REPORT_RLS: return "R";
+ case ACTIVATE: return "A";
+ case LAST_EVENT: ; // Fall through.
+ }
+
+ ASSERT_STRING("GetPointName: Unknown Event");
+ return NULL;
+}
+
+
+bool GetEventFromName(const char* name, Event* event) {
+ if (!event) {
+ ASSERT_STRING("GetEventFromName: event is NULL");
+ return false;
+ }
+ *event = INVALID_EVENT;
+ if (!name)
+ return false;
+
+ for (int i = INVALID_EVENT; i < LAST_EVENT; i++)
+ if (strcmp(name, GetEventName(static_cast<Event>(i))) == 0) {
+ *event = static_cast<Event>(i);
+ return true;
+ }
+
+ return false;
+}
+
+const char* GetProductName(Product product) {
+ switch (product) {
+ case IE_TOOLBAR: return "T";
+ case TOOLBAR_NOTIFIER: return "P";
+ case PACK: return "U";
+ case DESKTOP: return "D";
+ case CHROME: return "C";
+ case FF_TOOLBAR: return "B";
+ case QSB_WIN: return "K";
+ case WEBAPPS: return "W";
+ case PINYIN_IME: return "N";
+ case PARTNER: return "V";
+ }
+
+ ASSERT_STRING("GetProductName: Unknown Product");
+ return "";
+}
+
+} // namespace rlz_lib
diff --git a/chromium/rlz/lib/lib_values.h b/chromium/rlz/lib/lib_values.h
new file mode 100644
index 00000000000..05fbc6b25a1
--- /dev/null
+++ b/chromium/rlz/lib/lib_values.h
@@ -0,0 +1,99 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Key and value names of the location of the RLZ shared state.
+
+#ifndef RLZ_LIB_LIB_VALUES_H_
+#define RLZ_LIB_LIB_VALUES_H_
+
+#include "base/basictypes.h"
+#include "rlz/lib/rlz_enums.h"
+
+namespace rlz_lib {
+
+//
+// Ping CGI arguments:
+//
+// Events are reported as (without spaces):
+// kEventsCgiVariable = <AccessPoint1><Event1> kEventsCgiSeparator <P2><E2>...
+//
+// Event responses from the server look like:
+// kEventsCgiVariable : <AccessPoint1><Event1> kEventsCgiSeparator <P2><E2>...
+//
+// RLZ's are reported as (without spaces):
+// kRlzCgiVariable = <AccessPoint> <kRlzCgiIndicator> <RLZ value>
+// <kRlzCgiSeparator> <AP2><Indicator><V2><Separator> ....
+//
+// RLZ responses from the server look like (without spaces):
+// kRlzCgiVariable<Access Point> : <RLZ value>
+//
+// DCC if reported should look like (without spaces):
+// kDccCgiVariable = <DCC Value>
+//
+// RLS if reported should look like (without spaces):
+// kRlsCgiVariable = <RLS Value>
+//
+// Machine ID if reported should look like (without spaces):
+// kMachineIdCgiVariable = <Machine ID Value>
+//
+// A server response setting / confirming the DCC will look like (no spaces):
+// kDccCgiVariable : <DCC Value>
+//
+// Each ping to the server must also contain kProtocolCgiArgument as well.
+//
+// Pings may also contain (but not necessarily controlled by this Lib):
+// - The product signature: kProductSignatureCgiVariable = <signature>
+// - The product brand: kProductBrandCgiVariable = <brand>
+// - The product installation ID: kProductIdCgiVariable = <id>
+extern const char kEventsCgiVariable[];
+extern const char kStatefulEventsCgiVariable[];
+extern const char kEventsCgiSeparator;
+
+extern const char kDccCgiVariable[];
+extern const char kProtocolCgiArgument[];
+
+extern const char kProductSignatureCgiVariable[];
+extern const char kProductBrandCgiVariable[];
+extern const char kProductLanguageCgiVariable[];
+extern const char kProductIdCgiVariable[];
+
+extern const char kRlzCgiVariable[];
+extern const char kRlzCgiSeparator[];
+extern const char kRlzCgiIndicator[];
+
+extern const char kRlsCgiVariable[];
+extern const char kMachineIdCgiVariable[];
+extern const char kSetDccResponseVariable[];
+
+//
+// Financial ping server information.
+//
+
+extern const char kFinancialPingPath[];
+extern const char kFinancialServer[];
+
+extern const int kFinancialPort;
+
+extern const int64 kEventsPingInterval;
+extern const int64 kNoEventsPingInterval;
+
+extern const char kFinancialPingUserAgent[];
+extern const char* kFinancialPingResponseObjects[];
+
+//
+// The names for AccessPoints and Events that we use MUST be the same
+// as those used/understood by the server.
+//
+const char* GetAccessPointName(AccessPoint point);
+bool GetAccessPointFromName(const char* name, AccessPoint* point);
+
+const char* GetEventName(Event event);
+bool GetEventFromName(const char* name, Event* event);
+
+// The names for products are used only client-side.
+const char* GetProductName(Product product);
+
+} // namespace rlz_lib
+
+#endif // RLZ_LIB_LIB_VALUES_H_
diff --git a/chromium/rlz/lib/lib_values_unittest.cc b/chromium/rlz/lib/lib_values_unittest.cc
new file mode 100644
index 00000000000..7e8fa83b995
--- /dev/null
+++ b/chromium/rlz/lib/lib_values_unittest.cc
@@ -0,0 +1,61 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "rlz/lib/lib_values.h"
+
+#include "base/logging.h"
+#include "rlz/lib/assert.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(LibValuesUnittest, GetAccessPointFromName) {
+ rlz_lib::SetExpectedAssertion("GetAccessPointFromName: point is NULL");
+ EXPECT_FALSE(rlz_lib::GetAccessPointFromName("", NULL));
+ rlz_lib::SetExpectedAssertion("");
+
+ rlz_lib::AccessPoint point;
+ EXPECT_FALSE(rlz_lib::GetAccessPointFromName(NULL, &point));
+ EXPECT_EQ(rlz_lib::NO_ACCESS_POINT, point);
+
+ EXPECT_TRUE(rlz_lib::GetAccessPointFromName("", &point));
+ EXPECT_EQ(rlz_lib::NO_ACCESS_POINT, point);
+
+ EXPECT_FALSE(rlz_lib::GetAccessPointFromName("i1", &point));
+ EXPECT_EQ(rlz_lib::NO_ACCESS_POINT, point);
+
+ EXPECT_TRUE(rlz_lib::GetAccessPointFromName("I7", &point));
+ EXPECT_EQ(rlz_lib::IE_DEFAULT_SEARCH, point);
+
+ EXPECT_TRUE(rlz_lib::GetAccessPointFromName("T4", &point));
+ EXPECT_EQ(rlz_lib::IETB_SEARCH_BOX, point);
+
+ EXPECT_FALSE(rlz_lib::GetAccessPointFromName("T4 ", &point));
+ EXPECT_EQ(rlz_lib::NO_ACCESS_POINT, point);
+}
+
+
+TEST(LibValuesUnittest, GetEventFromName) {
+ rlz_lib::SetExpectedAssertion("GetEventFromName: event is NULL");
+ EXPECT_FALSE(rlz_lib::GetEventFromName("", NULL));
+ rlz_lib::SetExpectedAssertion("");
+
+ rlz_lib::Event event;
+ EXPECT_FALSE(rlz_lib::GetEventFromName(NULL, &event));
+ EXPECT_EQ(rlz_lib::INVALID_EVENT, event);
+
+ EXPECT_TRUE(rlz_lib::GetEventFromName("", &event));
+ EXPECT_EQ(rlz_lib::INVALID_EVENT, event);
+
+ EXPECT_FALSE(rlz_lib::GetEventFromName("i1", &event));
+ EXPECT_EQ(rlz_lib::INVALID_EVENT, event);
+
+ EXPECT_TRUE(rlz_lib::GetEventFromName("I", &event));
+ EXPECT_EQ(rlz_lib::INSTALL, event);
+
+ EXPECT_TRUE(rlz_lib::GetEventFromName("F", &event));
+ EXPECT_EQ(rlz_lib::FIRST_SEARCH, event);
+
+ EXPECT_FALSE(rlz_lib::GetEventFromName("F ", &event));
+ EXPECT_EQ(rlz_lib::INVALID_EVENT, event);
+}
diff --git a/chromium/rlz/lib/machine_id.cc b/chromium/rlz/lib/machine_id.cc
new file mode 100644
index 00000000000..b8c10f0ef32
--- /dev/null
+++ b/chromium/rlz/lib/machine_id.cc
@@ -0,0 +1,84 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "rlz/lib/machine_id.h"
+
+#include "base/sha1.h"
+#include "rlz/lib/assert.h"
+#include "rlz/lib/crc8.h"
+#include "rlz/lib/string_utils.h"
+
+namespace rlz_lib {
+
+bool GetMachineId(std::string* machine_id) {
+ if (!machine_id)
+ return false;
+
+ static std::string calculated_id;
+ static bool calculated = false;
+ if (calculated) {
+ *machine_id = calculated_id;
+ return true;
+ }
+
+ string16 sid_string;
+ int volume_id;
+ if (!GetRawMachineId(&sid_string, &volume_id))
+ return false;
+
+ if (!testing::GetMachineIdImpl(sid_string, volume_id, machine_id))
+ return false;
+
+ calculated = true;
+ calculated_id = *machine_id;
+ return true;
+}
+
+namespace testing {
+
+bool GetMachineIdImpl(const string16& sid_string,
+ int volume_id,
+ std::string* machine_id) {
+ machine_id->clear();
+
+ // The ID should be the SID hash + the Hard Drive SNo. + checksum byte.
+ static const int kSizeWithoutChecksum = base::kSHA1Length + sizeof(int);
+ std::basic_string<unsigned char> id_binary(kSizeWithoutChecksum + 1, 0);
+
+ if (!sid_string.empty()) {
+ // In order to be compatible with the old version of RLZ, the hash of the
+ // SID must be done with all the original bytes from the unicode string.
+ // However, the chromebase SHA1 hash function takes only an std::string as
+ // input, so the unicode string needs to be converted to std::string
+ // "as is".
+ size_t byte_count = sid_string.size() * sizeof(string16::value_type);
+ const char* buffer = reinterpret_cast<const char*>(sid_string.c_str());
+ std::string sid_string_buffer(buffer, byte_count);
+
+ // Note that digest can have embedded nulls.
+ std::string digest(base::SHA1HashString(sid_string_buffer));
+ VERIFY(digest.size() == base::kSHA1Length);
+ std::copy(digest.begin(), digest.end(), id_binary.begin());
+ }
+
+ // Convert from int to binary (makes big-endian).
+ for (size_t i = 0; i < sizeof(int); i++) {
+ int shift_bits = 8 * (sizeof(int) - i - 1);
+ id_binary[base::kSHA1Length + i] = static_cast<unsigned char>(
+ (volume_id >> shift_bits) & 0xFF);
+ }
+
+ // Append the checksum byte.
+ if (!sid_string.empty() || (0 != volume_id))
+ rlz_lib::Crc8::Generate(id_binary.c_str(),
+ kSizeWithoutChecksum,
+ &id_binary[kSizeWithoutChecksum]);
+
+ return rlz_lib::BytesToString(
+ id_binary.c_str(), kSizeWithoutChecksum + 1, machine_id);
+}
+
+} // namespace testing
+
+} // namespace rlz_lib
diff --git a/chromium/rlz/lib/machine_id.h b/chromium/rlz/lib/machine_id.h
new file mode 100644
index 00000000000..af58c8ed927
--- /dev/null
+++ b/chromium/rlz/lib/machine_id.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RLZ_LIB_MACHINE_ID_H_
+#define RLZ_LIB_MACHINE_ID_H_
+
+#include "base/strings/string16.h"
+
+#include <string>
+
+namespace rlz_lib {
+
+// Gets the unique ID for the machine used for RLZ tracking purposes. On
+// Windows, this ID is derived from the Windows machine SID, and is the string
+// representation of a 20 byte hash + 4 bytes volum id + a 1 byte checksum.
+// Included in financial pings with events, unless explicitly forbidden by the
+// calling application.
+bool GetMachineId(std::string* machine_id);
+
+// Retrieves a raw machine identifier string and a machine-specific
+// 4 byte value. GetMachineId() will SHA1 |data|, append |more_data|, compute
+// the Crc8 of that, and return a hex-encoded string of that data.
+bool GetRawMachineId(string16* data, int* more_data);
+
+namespace testing {
+bool GetMachineIdImpl(const string16& sid_string,
+ int volume_id,
+ std::string* machine_id);
+} // namespace testing
+
+} // namespace rlz_lib
+
+#endif // RLZ_LIB_MACHINE_ID_H_
diff --git a/chromium/rlz/lib/machine_id_unittest.cc b/chromium/rlz/lib/machine_id_unittest.cc
new file mode 100644
index 00000000000..c01e9be618a
--- /dev/null
+++ b/chromium/rlz/lib/machine_id_unittest.cc
@@ -0,0 +1,20 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "rlz/lib/machine_id.h"
+
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "rlz/test/rlz_test_helpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// This test will fail if the behavior of GetMachineId changes.
+TEST(MachineDealCodeTestMachineId, MachineId) {
+ string16 computer_sid(UTF8ToUTF16(
+ "S-1-5-21-2345599882-2448789067-1921365677"));
+ std::string id;
+ rlz_lib::testing::GetMachineIdImpl(computer_sid, 2651229008, &id);
+ EXPECT_STREQ("A341BA986A7E86840688977FCF20C86E253F00919E068B50F8",
+ id.c_str());
+}
diff --git a/chromium/rlz/lib/recursive_cross_process_lock_posix.cc b/chromium/rlz/lib/recursive_cross_process_lock_posix.cc
new file mode 100644
index 00000000000..8b271f123eb
--- /dev/null
+++ b/chromium/rlz/lib/recursive_cross_process_lock_posix.cc
@@ -0,0 +1,82 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "rlz/lib/recursive_cross_process_lock_posix.h"
+
+#include <fcntl.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+namespace rlz_lib {
+
+bool RecursiveCrossProcessLock::TryGetCrossProcessLock(
+ const base::FilePath& lock_filename) {
+ bool just_got_lock = false;
+
+ // Emulate a recursive mutex with a non-recursive one.
+ if (pthread_mutex_trylock(&recursive_lock_) == EBUSY) {
+ if (pthread_equal(pthread_self(), locking_thread_) == 0) {
+ // Some other thread has the lock, wait for it.
+ pthread_mutex_lock(&recursive_lock_);
+ CHECK(locking_thread_ == 0);
+ just_got_lock = true;
+ }
+ } else {
+ just_got_lock = true;
+ }
+
+ locking_thread_ = pthread_self();
+
+ // Try to acquire file lock.
+ if (just_got_lock) {
+ const int kMaxTimeoutMS = 5000; // Matches Windows.
+ const int kSleepPerTryMS = 200;
+
+ CHECK(file_lock_ == -1);
+ file_lock_ = open(lock_filename.value().c_str(), O_RDWR | O_CREAT, 0666);
+ if (file_lock_ == -1) {
+ perror("open");
+ return false;
+ }
+
+ int flock_result = -1;
+ int elapsed_ms = 0;
+ while ((flock_result =
+ HANDLE_EINTR(flock(file_lock_, LOCK_EX | LOCK_NB))) == -1 &&
+ errno == EWOULDBLOCK &&
+ elapsed_ms < kMaxTimeoutMS) {
+ usleep(kSleepPerTryMS * 1000);
+ elapsed_ms += kSleepPerTryMS;
+ }
+
+ if (flock_result == -1) {
+ perror("flock");
+ ignore_result(HANDLE_EINTR(close(file_lock_)));
+ file_lock_ = -1;
+ return false;
+ }
+ return true;
+ } else {
+ return file_lock_ != -1;
+ }
+}
+
+void RecursiveCrossProcessLock::ReleaseLock() {
+ if (file_lock_ != -1) {
+ ignore_result(HANDLE_EINTR(flock(file_lock_, LOCK_UN)));
+ ignore_result(HANDLE_EINTR(close(file_lock_)));
+ file_lock_ = -1;
+ }
+
+ locking_thread_ = 0;
+ pthread_mutex_unlock(&recursive_lock_);
+}
+
+} // namespace rlz_lib
diff --git a/chromium/rlz/lib/recursive_cross_process_lock_posix.h b/chromium/rlz/lib/recursive_cross_process_lock_posix.h
new file mode 100644
index 00000000000..69a2c7a136b
--- /dev/null
+++ b/chromium/rlz/lib/recursive_cross_process_lock_posix.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RLZ_LIB_RECURSIVE_CROSS_PROCESS_LOCK_POSIX_H_
+#define RLZ_LIB_RECURSIVE_CROSS_PROCESS_LOCK_POSIX_H_
+
+#include <pthread.h>
+
+namespace base {
+class FilePath;
+}
+
+namespace rlz_lib {
+
+// Creating a recursive cross-process mutex on Windows is one line. On POSIX,
+// there's no primitive for that, so this lock is emulated by an in-process
+// mutex to get the recursive part, followed by a cross-process lock for the
+// cross-process part.
+// This is a struct so that it doesn't need a static initializer.
+struct RecursiveCrossProcessLock {
+ // Tries to acquire a recursive cross-process lock. Note that this _always_
+ // acquires the in-process lock (if it wasn't already acquired). The parent
+ // directory of |lock_file| must exist.
+ bool TryGetCrossProcessLock(const base::FilePath& lock_filename);
+
+ // Releases the lock. Should always be called, even if
+ // TryGetCrossProcessLock() returned |false|.
+ void ReleaseLock();
+
+ pthread_mutex_t recursive_lock_;
+ pthread_t locking_thread_;
+
+ int file_lock_;
+};
+
+// On Mac, PTHREAD_RECURSIVE_MUTEX_INITIALIZER doesn't exist before 10.7 and
+// is buggy on 10.7 (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51906#c34),
+// so emulate recursive locking with a normal non-recursive mutex.
+#define RECURSIVE_CROSS_PROCESS_LOCK_INITIALIZER \
+ { PTHREAD_MUTEX_INITIALIZER, 0, -1 }
+
+} // namespace rlz_lib
+
+#endif // RLZ_LIB_RECURSIVE_CROSS_PROCESS_LOCK_POSIX_H_
diff --git a/chromium/rlz/lib/rlz_enums.h b/chromium/rlz/lib/rlz_enums.h
new file mode 100644
index 00000000000..0fd0513cc01
--- /dev/null
+++ b/chromium/rlz/lib/rlz_enums.h
@@ -0,0 +1,134 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RLZ_LIB_RLZ_ENUMS_H_
+#define RLZ_LIB_RLZ_ENUMS_H_
+
+namespace rlz_lib {
+
+// An Access Point offers a way to search using Google.
+enum AccessPoint {
+ NO_ACCESS_POINT = 0,
+
+ // Access points on Windows PCs.
+ IE_DEFAULT_SEARCH, // The IE7+ chrome search box next to the address bar.
+ IE_HOME_PAGE, // Search box on IE 5+ primary home page when Google.
+ IETB_SEARCH_BOX, // IE Toolbar v4+ search box.
+ QUICK_SEARCH_BOX, // Search box brought up by ctrl-ctrl key sequence,
+ // distributed as a part of Google Desktop
+ GD_DESKBAND, // Search box in deskbar when GD in deskbar mode.
+ GD_SEARCH_GADGET, // Search gadget when GD in sidebar mode.
+ GD_WEB_SERVER, // Boxes in web pages shown by local GD web server.
+ GD_OUTLOOK, // Search box installed within outlook by GD.
+ CHROME_OMNIBOX, // Chrome searches through the address bar omnibox (Win).
+ CHROME_HOME_PAGE, // Chrome searches through Google as home page (Win).
+ FFTB2_BOX, // Firefox Toolbar v2 Search Box.
+ FFTB3_BOX, // Firefox Toolbar v3+ Search Box.
+ PINYIN_IME_BHO, // Goopy Input Method Editor BHO (Pinyin).
+ IGOOGLE_WEBPAGE, // Searches on iGoogle through partner deals.
+
+ // Mobile idle screen search for different platforms.
+ MOBILE_IDLE_SCREEN_BLACKBERRY,
+ MOBILE_IDLE_SCREEN_WINMOB,
+ MOBILE_IDLE_SCREEN_SYMBIAN,
+
+ FF_HOME_PAGE, // Firefox home page when set to Google.
+ FF_SEARCH_BOX, // Firefox search box when set to Google.
+ IE_BROWSED_PAGE, // Search made in IE through user action (no product).
+ QSB_WIN_BOX, // Search box brought up by ctrl+space by default,
+ // distributed by toolbar and separate from the GD QSB
+ WEBAPPS_CALENDAR, // Webapps use of calendar.
+ WEBAPPS_DOCS, // Webapps use of writely.
+ WEBAPPS_GMAIL, // Webapps use of Gmail.
+
+ IETB_LINKDOCTOR, // Linkdoctor of IE Toolbar
+ FFTB_LINKDOCTOR, // Linkdoctor of FF Toolbar
+ IETB7_SEARCH_BOX, // IE Toolbar search box.
+ TB8_SEARCH_BOX, // IE/FF Toolbar search box.
+ CHROME_FRAME, // Chrome Frame.
+
+ // Partner access points.
+ PARTNER_AP_1,
+ PARTNER_AP_2,
+ PARTNER_AP_3,
+ PARTNER_AP_4,
+ PARTNER_AP_5,
+
+ CHROME_MAC_OMNIBOX, // Chrome searches through the address bar omnibox (Mac).
+ CHROME_MAC_HOME_PAGE,// Chrome searches through Google as home page (Mac).
+
+ CHROMEOS_OMNIBOX, // ChromeOS searches through the address bar omnibox.
+ CHROMEOS_HOME_PAGE, // ChromeOS searches through Google as home page.
+ CHROMEOS_RESERVED, // Reserved for ChromeOS.
+
+ // Unclaimed access points - should be used first before creating new APs.
+ // Please also make sure you re-name the enum before using an unclaimed value;
+ // this acts as a check to ensure we don't have collisions.
+ UNDEFINED_AP_M,
+ UNDEFINED_AP_N,
+ UNDEFINED_AP_O,
+ UNDEFINED_AP_P,
+ UNDEFINED_AP_Q,
+ UNDEFINED_AP_R,
+ UNDEFINED_AP_S,
+ UNDEFINED_AP_T,
+ UNDEFINED_AP_U,
+ UNDEFINED_AP_V,
+ UNDEFINED_AP_W,
+ UNDEFINED_AP_X,
+ UNDEFINED_AP_Y,
+ UNDEFINED_AP_Z,
+
+ PACK_AP0,
+ PACK_AP1,
+ PACK_AP2,
+ PACK_AP3,
+ PACK_AP4,
+ PACK_AP5,
+ PACK_AP6,
+ PACK_AP7,
+ PACK_AP8,
+ PACK_AP9,
+ PACK_AP10,
+ PACK_AP11,
+ PACK_AP12,
+ PACK_AP13,
+
+ // New Access Points should be added here without changing existing enums,
+ // (i.e. before LAST_ACCESS_POINT)
+ LAST_ACCESS_POINT
+};
+
+// A product is an entity which wants to gets credit for setting
+// an Access Point.
+enum Product {
+ IE_TOOLBAR = 1,
+ TOOLBAR_NOTIFIER,
+ PACK,
+ DESKTOP,
+ CHROME,
+ FF_TOOLBAR,
+ QSB_WIN,
+ WEBAPPS,
+ PINYIN_IME,
+ PARTNER
+ // New Products should be added here without changing existing enums.
+};
+
+// Events that note Product and Access Point modifications.
+enum Event {
+ INVALID_EVENT = 0,
+ INSTALL = 1, // Access Point added to the system.
+ SET_TO_GOOGLE, // Point set from non-Google provider to Google.
+ FIRST_SEARCH, // First search from point since INSTALL
+ REPORT_RLS, // Report old system "RLS" financial value for this point.
+ // New Events should be added here without changing existing enums,
+ // before LAST_EVENT.
+ ACTIVATE, // Product being used for a period of time.
+ LAST_EVENT
+};
+
+} // namespace rlz_lib
+
+#endif // RLZ_LIB_RLZ_ENUMS_H_
diff --git a/chromium/rlz/lib/rlz_lib.cc b/chromium/rlz/lib/rlz_lib.cc
new file mode 100644
index 00000000000..b204fe902cf
--- /dev/null
+++ b/chromium/rlz/lib/rlz_lib.cc
@@ -0,0 +1,651 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A library to manage RLZ information for access-points shared
+// across different client applications.
+
+#include "rlz/lib/rlz_lib.h"
+
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "rlz/lib/assert.h"
+#include "rlz/lib/crc32.h"
+#include "rlz/lib/financial_ping.h"
+#include "rlz/lib/lib_values.h"
+#include "rlz/lib/rlz_value_store.h"
+#include "rlz/lib/string_utils.h"
+
+namespace {
+
+// Event information returned from ping response.
+struct ReturnedEvent {
+ rlz_lib::AccessPoint access_point;
+ rlz_lib::Event event_type;
+};
+
+// Helper functions
+
+bool IsAccessPointSupported(rlz_lib::AccessPoint point) {
+ switch (point) {
+ case rlz_lib::NO_ACCESS_POINT:
+ case rlz_lib::LAST_ACCESS_POINT:
+
+ case rlz_lib::MOBILE_IDLE_SCREEN_BLACKBERRY:
+ case rlz_lib::MOBILE_IDLE_SCREEN_WINMOB:
+ case rlz_lib::MOBILE_IDLE_SCREEN_SYMBIAN:
+ // These AP's are never available on Windows PCs.
+ return false;
+
+ case rlz_lib::IE_DEFAULT_SEARCH:
+ case rlz_lib::IE_HOME_PAGE:
+ case rlz_lib::IETB_SEARCH_BOX:
+ case rlz_lib::QUICK_SEARCH_BOX:
+ case rlz_lib::GD_DESKBAND:
+ case rlz_lib::GD_SEARCH_GADGET:
+ case rlz_lib::GD_WEB_SERVER:
+ case rlz_lib::GD_OUTLOOK:
+ case rlz_lib::CHROME_OMNIBOX:
+ case rlz_lib::CHROME_HOME_PAGE:
+ // TODO: Figure out when these settings are set to Google.
+
+ default:
+ return true;
+ }
+}
+
+// Current RLZ can only use [a-zA-Z0-9_\-]
+// We will be more liberal and allow some additional chars, but not url meta
+// chars.
+bool IsGoodRlzChar(const char ch) {
+ if (IsAsciiAlpha(ch) || IsAsciiDigit(ch))
+ return true;
+
+ switch (ch) {
+ case '_':
+ case '-':
+ case '!':
+ case '@':
+ case '$':
+ case '*':
+ case '(':
+ case ')':
+ case ';':
+ case '.':
+ case '<':
+ case '>':
+ return true;
+ }
+
+ return false;
+}
+
+// This function will remove bad rlz chars and also limit the max rlz to some
+// reasonable size. It also assumes that normalized_rlz is at least
+// kMaxRlzLength+1 long.
+void NormalizeRlz(const char* raw_rlz, char* normalized_rlz) {
+ size_t index = 0;
+ for (; raw_rlz[index] != 0 && index < rlz_lib::kMaxRlzLength; ++index) {
+ char current = raw_rlz[index];
+ if (IsGoodRlzChar(current)) {
+ normalized_rlz[index] = current;
+ } else {
+ normalized_rlz[index] = '.';
+ }
+ }
+
+ normalized_rlz[index] = 0;
+}
+
+void GetEventsFromResponseString(
+ const std::string& response_line,
+ const std::string& field_header,
+ std::vector<ReturnedEvent>* event_array) {
+ // Get the string of events.
+ std::string events = response_line.substr(field_header.size());
+ TrimWhitespaceASCII(events, TRIM_LEADING, &events);
+
+ int events_length = events.find_first_of("\r\n ");
+ if (events_length < 0)
+ events_length = events.size();
+ events = events.substr(0, events_length);
+
+ // Break this up into individual events
+ int event_end_index = -1;
+ do {
+ int event_begin = event_end_index + 1;
+ event_end_index = events.find(rlz_lib::kEventsCgiSeparator, event_begin);
+ int event_end = event_end_index;
+ if (event_end < 0)
+ event_end = events_length;
+
+ std::string event_string = events.substr(event_begin,
+ event_end - event_begin);
+ if (event_string.size() != 3) // 3 = 2(AP) + 1(E)
+ continue;
+
+ rlz_lib::AccessPoint point = rlz_lib::NO_ACCESS_POINT;
+ rlz_lib::Event event = rlz_lib::INVALID_EVENT;
+ if (!GetAccessPointFromName(event_string.substr(0, 2).c_str(), &point) ||
+ point == rlz_lib::NO_ACCESS_POINT) {
+ continue;
+ }
+
+ if (!GetEventFromName(event_string.substr(event_string.size() - 1).c_str(),
+ &event) || event == rlz_lib::INVALID_EVENT) {
+ continue;
+ }
+
+ ReturnedEvent current_event = {point, event};
+ event_array->push_back(current_event);
+ } while (event_end_index >= 0);
+}
+
+// Event storage functions.
+bool RecordStatefulEvent(rlz_lib::Product product, rlz_lib::AccessPoint point,
+ rlz_lib::Event event) {
+ rlz_lib::ScopedRlzValueStoreLock lock;
+ rlz_lib::RlzValueStore* store = lock.GetStore();
+ if (!store || !store->HasAccess(rlz_lib::RlzValueStore::kWriteAccess))
+ return false;
+
+ // Write the new event to the value store.
+ const char* point_name = GetAccessPointName(point);
+ const char* event_name = GetEventName(event);
+ if (!point_name || !event_name)
+ return false;
+
+ if (!point_name[0] || !event_name[0])
+ return false;
+
+ std::string new_event_value;
+ base::StringAppendF(&new_event_value, "%s%s", point_name, event_name);
+ return store->AddStatefulEvent(product, new_event_value.c_str());
+}
+
+bool GetProductEventsAsCgiHelper(rlz_lib::Product product, char* cgi,
+ size_t cgi_size,
+ rlz_lib::RlzValueStore* store) {
+ // Prepend the CGI param key to the buffer.
+ std::string cgi_arg;
+ base::StringAppendF(&cgi_arg, "%s=", rlz_lib::kEventsCgiVariable);
+ if (cgi_size <= cgi_arg.size())
+ return false;
+
+ size_t index;
+ for (index = 0; index < cgi_arg.size(); ++index)
+ cgi[index] = cgi_arg[index];
+
+ // Read stored events.
+ std::vector<std::string> events;
+ if (!store->ReadProductEvents(product, &events))
+ return false;
+
+ // Append the events to the buffer.
+ size_t num_values = 0;
+
+ for (num_values = 0; num_values < events.size(); ++num_values) {
+ cgi[index] = '\0';
+
+ int divider = num_values > 0 ? 1 : 0;
+ int size = cgi_size - (index + divider);
+ if (size <= 0)
+ return cgi_size >= (rlz_lib::kMaxCgiLength + 1);
+
+ strncpy(cgi + index + divider, events[num_values].c_str(), size);
+ if (divider)
+ cgi[index] = rlz_lib::kEventsCgiSeparator;
+
+ index += std::min((int)events[num_values].length(), size) + divider;
+ }
+
+ cgi[index] = '\0';
+
+ return num_values > 0;
+}
+
+} // namespace
+
+namespace rlz_lib {
+
+#if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
+bool SetURLRequestContext(net::URLRequestContextGetter* context) {
+ return FinancialPing::SetURLRequestContext(context);
+}
+#endif
+
+bool GetProductEventsAsCgi(Product product, char* cgi, size_t cgi_size) {
+ if (!cgi || cgi_size <= 0) {
+ ASSERT_STRING("GetProductEventsAsCgi: Invalid buffer");
+ return false;
+ }
+
+ cgi[0] = 0;
+
+ ScopedRlzValueStoreLock lock;
+ RlzValueStore* store = lock.GetStore();
+ if (!store || !store->HasAccess(RlzValueStore::kReadAccess))
+ return false;
+
+ size_t size_local = std::min(
+ static_cast<size_t>(kMaxCgiLength + 1), cgi_size);
+ bool result = GetProductEventsAsCgiHelper(product, cgi, size_local, store);
+
+ if (!result) {
+ ASSERT_STRING("GetProductEventsAsCgi: Possibly insufficient buffer size");
+ cgi[0] = 0;
+ return false;
+ }
+
+ return true;
+}
+
+bool RecordProductEvent(Product product, AccessPoint point, Event event) {
+ ScopedRlzValueStoreLock lock;
+ RlzValueStore* store = lock.GetStore();
+ if (!store || !store->HasAccess(RlzValueStore::kWriteAccess))
+ return false;
+
+ // Get this event's value.
+ const char* point_name = GetAccessPointName(point);
+ const char* event_name = GetEventName(event);
+ if (!point_name || !event_name)
+ return false;
+
+ if (!point_name[0] || !event_name[0])
+ return false;
+
+ std::string new_event_value;
+ base::StringAppendF(&new_event_value, "%s%s", point_name, event_name);
+
+ // Check whether this event is a stateful event. If so, don't record it.
+ if (store->IsStatefulEvent(product, new_event_value.c_str())) {
+ // For a stateful event we skip recording, this function is also
+ // considered successful.
+ return true;
+ }
+
+ // Write the new event to the value store.
+ return store->AddProductEvent(product, new_event_value.c_str());
+}
+
+bool ClearProductEvent(Product product, AccessPoint point, Event event) {
+ ScopedRlzValueStoreLock lock;
+ RlzValueStore* store = lock.GetStore();
+ if (!store || !store->HasAccess(RlzValueStore::kWriteAccess))
+ return false;
+
+ // Get the event's value store value and delete it.
+ const char* point_name = GetAccessPointName(point);
+ const char* event_name = GetEventName(event);
+ if (!point_name || !event_name)
+ return false;
+
+ if (!point_name[0] || !event_name[0])
+ return false;
+
+ std::string event_value;
+ base::StringAppendF(&event_value, "%s%s", point_name, event_name);
+ return store->ClearProductEvent(product, event_value.c_str());
+}
+
+// RLZ storage functions.
+
+bool GetAccessPointRlz(AccessPoint point, char* rlz, size_t rlz_size) {
+ if (!rlz || rlz_size <= 0) {
+ ASSERT_STRING("GetAccessPointRlz: Invalid buffer");
+ return false;
+ }
+
+ rlz[0] = 0;
+
+ ScopedRlzValueStoreLock lock;
+ RlzValueStore* store = lock.GetStore();
+ if (!store || !store->HasAccess(RlzValueStore::kReadAccess))
+ return false;
+
+ if (!IsAccessPointSupported(point))
+ return false;
+
+ return store->ReadAccessPointRlz(point, rlz, rlz_size);
+}
+
+bool SetAccessPointRlz(AccessPoint point, const char* new_rlz) {
+ ScopedRlzValueStoreLock lock;
+ RlzValueStore* store = lock.GetStore();
+ if (!store || !store->HasAccess(RlzValueStore::kWriteAccess))
+ return false;
+
+ if (!new_rlz) {
+ ASSERT_STRING("SetAccessPointRlz: Invalid buffer");
+ return false;
+ }
+
+ // Return false if the access point is not set to Google.
+ if (!IsAccessPointSupported(point)) {
+ ASSERT_STRING(("SetAccessPointRlz: "
+ "Cannot set RLZ for unsupported access point."));
+ return false;
+ }
+
+ // Verify the RLZ length.
+ size_t rlz_length = strlen(new_rlz);
+ if (rlz_length > kMaxRlzLength) {
+ ASSERT_STRING("SetAccessPointRlz: RLZ length is exceeds max allowed.");
+ return false;
+ }
+
+ char normalized_rlz[kMaxRlzLength + 1];
+ NormalizeRlz(new_rlz, normalized_rlz);
+ VERIFY(strlen(new_rlz) == rlz_length);
+
+ // Setting RLZ to empty == clearing.
+ if (normalized_rlz[0] == 0)
+ return store->ClearAccessPointRlz(point);
+ return store->WriteAccessPointRlz(point, normalized_rlz);
+}
+
+// Financial Server pinging functions.
+
+bool FormFinancialPingRequest(Product product, const AccessPoint* access_points,
+ const char* product_signature,
+ const char* product_brand,
+ const char* product_id,
+ const char* product_lang,
+ bool exclude_machine_id,
+ char* request, size_t request_buffer_size) {
+ if (!request || request_buffer_size == 0)
+ return false;
+
+ request[0] = 0;
+
+ std::string request_string;
+ if (!FinancialPing::FormRequest(product, access_points, product_signature,
+ product_brand, product_id, product_lang,
+ exclude_machine_id, &request_string))
+ return false;
+
+ if (request_string.size() >= request_buffer_size)
+ return false;
+
+ strncpy(request, request_string.c_str(), request_buffer_size);
+ request[request_buffer_size - 1] = 0;
+ return true;
+}
+
+bool PingFinancialServer(Product product, const char* request, char* response,
+ size_t response_buffer_size) {
+ if (!response || response_buffer_size == 0)
+ return false;
+ response[0] = 0;
+
+ // Check if the time is right to ping.
+ if (!FinancialPing::IsPingTime(product, false))
+ return false;
+
+ // Send out the ping.
+ std::string response_string;
+ if (!FinancialPing::PingServer(request, &response_string))
+ return false;
+
+ if (response_string.size() >= response_buffer_size)
+ return false;
+
+ strncpy(response, response_string.c_str(), response_buffer_size);
+ response[response_buffer_size - 1] = 0;
+ return true;
+}
+
+bool IsPingResponseValid(const char* response, int* checksum_idx) {
+ if (!response || !response[0])
+ return false;
+
+ if (checksum_idx)
+ *checksum_idx = -1;
+
+ if (strlen(response) > kMaxPingResponseLength) {
+ ASSERT_STRING("IsPingResponseValid: response is too long to parse.");
+ return false;
+ }
+
+ // Find the checksum line.
+ std::string response_string(response);
+
+ std::string checksum_param("\ncrc32: ");
+ int calculated_crc;
+ int checksum_index = response_string.find(checksum_param);
+ if (checksum_index >= 0) {
+ // Calculate checksum of message preceeding checksum line.
+ // (+ 1 to include the \n)
+ std::string message(response_string.substr(0, checksum_index + 1));
+ if (!Crc32(message.c_str(), &calculated_crc))
+ return false;
+ } else {
+ checksum_param = "crc32: "; // Empty response case.
+ if (!StartsWithASCII(response_string, checksum_param, true))
+ return false;
+
+ checksum_index = 0;
+ if (!Crc32("", &calculated_crc))
+ return false;
+ }
+
+ // Find the checksum value on the response.
+ int checksum_end = response_string.find("\n", checksum_index + 1);
+ if (checksum_end < 0)
+ checksum_end = response_string.size();
+
+ int checksum_begin = checksum_index + checksum_param.size();
+ std::string checksum = response_string.substr(checksum_begin,
+ checksum_end - checksum_begin + 1);
+ TrimWhitespaceASCII(checksum, TRIM_ALL, &checksum);
+
+ if (checksum_idx)
+ *checksum_idx = checksum_index;
+
+ return calculated_crc == HexStringToInteger(checksum.c_str());
+}
+
+// Complex helpers built on top of other functions.
+
+bool ParseFinancialPingResponse(Product product, const char* response) {
+ // Update the last ping time irrespective of success.
+ FinancialPing::UpdateLastPingTime(product);
+ // Parse the ping response - update RLZs, clear events.
+ return ParsePingResponse(product, response);
+}
+
+bool SendFinancialPing(Product product, const AccessPoint* access_points,
+ const char* product_signature,
+ const char* product_brand,
+ const char* product_id, const char* product_lang,
+ bool exclude_machine_id) {
+ return SendFinancialPing(product, access_points, product_signature,
+ product_brand, product_id, product_lang,
+ exclude_machine_id, false);
+}
+
+
+bool SendFinancialPing(Product product, const AccessPoint* access_points,
+ const char* product_signature,
+ const char* product_brand,
+ const char* product_id, const char* product_lang,
+ bool exclude_machine_id,
+ const bool skip_time_check) {
+ // Create the financial ping request.
+ std::string request;
+ if (!FinancialPing::FormRequest(product, access_points, product_signature,
+ product_brand, product_id, product_lang,
+ exclude_machine_id, &request))
+ return false;
+
+ // Check if the time is right to ping.
+ if (!FinancialPing::IsPingTime(product, skip_time_check))
+ return false;
+
+ // Send out the ping, update the last ping time irrespective of success.
+ FinancialPing::UpdateLastPingTime(product);
+ std::string response;
+ if (!FinancialPing::PingServer(request.c_str(), &response))
+ return false;
+
+ // Parse the ping response - update RLZs, clear events.
+ return ParsePingResponse(product, response.c_str());
+}
+
+// TODO: Use something like RSA to make sure the response is
+// from a Google server.
+bool ParsePingResponse(Product product, const char* response) {
+ rlz_lib::ScopedRlzValueStoreLock lock;
+ rlz_lib::RlzValueStore* store = lock.GetStore();
+ if (!store || !store->HasAccess(rlz_lib::RlzValueStore::kWriteAccess))
+ return false;
+
+ std::string response_string(response);
+ int response_length = -1;
+ if (!IsPingResponseValid(response, &response_length))
+ return false;
+
+ if (0 == response_length)
+ return true; // Empty response - no parsing.
+
+ std::string events_variable;
+ std::string stateful_events_variable;
+ base::SStringPrintf(&events_variable, "%s: ", kEventsCgiVariable);
+ base::SStringPrintf(&stateful_events_variable, "%s: ",
+ kStatefulEventsCgiVariable);
+
+ int rlz_cgi_length = strlen(kRlzCgiVariable);
+
+ // Split response lines. Expected response format is lines of the form:
+ // rlzW1: 1R1_____en__252
+ int line_end_index = -1;
+ do {
+ int line_begin = line_end_index + 1;
+ line_end_index = response_string.find("\n", line_begin);
+
+ int line_end = line_end_index;
+ if (line_end < 0)
+ line_end = response_length;
+
+ if (line_end <= line_begin)
+ continue; // Empty line.
+
+ std::string response_line;
+ response_line = response_string.substr(line_begin, line_end - line_begin);
+
+ if (StartsWithASCII(response_line, kRlzCgiVariable, true)) { // An RLZ.
+ int separator_index = -1;
+ if ((separator_index = response_line.find(": ")) < 0)
+ continue; // Not a valid key-value pair.
+
+ // Get the access point.
+ std::string point_name =
+ response_line.substr(3, separator_index - rlz_cgi_length);
+ AccessPoint point = NO_ACCESS_POINT;
+ if (!GetAccessPointFromName(point_name.c_str(), &point) ||
+ point == NO_ACCESS_POINT)
+ continue; // Not a valid access point.
+
+ // Get the new RLZ.
+ std::string rlz_value(response_line.substr(separator_index + 2));
+ TrimWhitespaceASCII(rlz_value, TRIM_LEADING, &rlz_value);
+
+ size_t rlz_length = rlz_value.find_first_of("\r\n ");
+ if (rlz_length == std::string::npos)
+ rlz_length = rlz_value.size();
+
+ if (rlz_length > kMaxRlzLength)
+ continue; // Too long.
+
+ if (IsAccessPointSupported(point))
+ SetAccessPointRlz(point, rlz_value.substr(0, rlz_length).c_str());
+ } else if (StartsWithASCII(response_line, events_variable, true)) {
+ // Clear events which server parsed.
+ std::vector<ReturnedEvent> event_array;
+ GetEventsFromResponseString(response_line, events_variable, &event_array);
+ for (size_t i = 0; i < event_array.size(); ++i) {
+ ClearProductEvent(product, event_array[i].access_point,
+ event_array[i].event_type);
+ }
+ } else if (StartsWithASCII(response_line, stateful_events_variable, true)) {
+ // Record any stateful events the server send over.
+ std::vector<ReturnedEvent> event_array;
+ GetEventsFromResponseString(response_line, stateful_events_variable,
+ &event_array);
+ for (size_t i = 0; i < event_array.size(); ++i) {
+ RecordStatefulEvent(product, event_array[i].access_point,
+ event_array[i].event_type);
+ }
+ }
+ } while (line_end_index >= 0);
+
+#if defined(OS_WIN)
+ // Update the DCC in registry if needed.
+ SetMachineDealCodeFromPingResponse(response);
+#endif
+
+ return true;
+}
+
+bool GetPingParams(Product product, const AccessPoint* access_points,
+ char* cgi, size_t cgi_size) {
+ if (!cgi || cgi_size <= 0) {
+ ASSERT_STRING("GetPingParams: Invalid buffer");
+ return false;
+ }
+
+ cgi[0] = 0;
+
+ if (!access_points) {
+ ASSERT_STRING("GetPingParams: access_points is NULL");
+ return false;
+ }
+
+ // Add the RLZ Exchange Protocol version.
+ std::string cgi_string(kProtocolCgiArgument);
+
+ // Copy the &rlz= over.
+ base::StringAppendF(&cgi_string, "&%s=", kRlzCgiVariable);
+
+ {
+ // Now add each of the RLZ's. Keep the lock during all GetAccessPointRlz()
+ // calls below.
+ ScopedRlzValueStoreLock lock;
+ RlzValueStore* store = lock.GetStore();
+ if (!store || !store->HasAccess(RlzValueStore::kReadAccess))
+ return false;
+ bool first_rlz = true; // comma before every RLZ but the first.
+ for (int i = 0; access_points[i] != NO_ACCESS_POINT; i++) {
+ char rlz[kMaxRlzLength + 1];
+ if (GetAccessPointRlz(access_points[i], rlz, arraysize(rlz))) {
+ const char* access_point = GetAccessPointName(access_points[i]);
+ if (!access_point)
+ continue;
+
+ base::StringAppendF(&cgi_string, "%s%s%s%s",
+ first_rlz ? "" : kRlzCgiSeparator,
+ access_point, kRlzCgiIndicator, rlz);
+ first_rlz = false;
+ }
+ }
+
+#if defined(OS_WIN)
+ // Report the DCC too if not empty. DCCs are windows-only.
+ char dcc[kMaxDccLength + 1];
+ dcc[0] = 0;
+ if (GetMachineDealCode(dcc, arraysize(dcc)) && dcc[0])
+ base::StringAppendF(&cgi_string, "&%s=%s", kDccCgiVariable, dcc);
+#endif
+ }
+
+ if (cgi_string.size() >= cgi_size)
+ return false;
+
+ strncpy(cgi, cgi_string.c_str(), cgi_size);
+ cgi[cgi_size - 1] = 0;
+
+ return true;
+}
+
+} // namespace rlz_lib
diff --git a/chromium/rlz/lib/rlz_lib.h b/chromium/rlz/lib/rlz_lib.h
new file mode 100644
index 00000000000..1f8be5ca2d5
--- /dev/null
+++ b/chromium/rlz/lib/rlz_lib.h
@@ -0,0 +1,335 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A library to manage RLZ information for access-points shared
+// across different client applications.
+//
+// All functions return true on success and false on error.
+// This implemenation is thread safe.
+
+
+#ifndef RLZ_LIB_RLZ_LIB_H_
+#define RLZ_LIB_RLZ_LIB_H_
+
+#include <stdio.h>
+#include <string>
+
+#include "build/build_config.h"
+
+#include "rlz/lib/rlz_enums.h"
+
+#if defined(OS_WIN)
+#define RLZ_LIB_API __cdecl
+#else
+#define RLZ_LIB_API
+#endif
+
+// Define one of
+// + RLZ_NETWORK_IMPLEMENTATION_WIN_INET: Uses win inet to send financial pings.
+// + RLZ_NETWORK_IMPLEMENTATION_CHROME_NET: Uses chrome's network stack to send
+// financial pings. rlz_lib::SetURLRequestContext() must be called before
+// any calls to SendFinancialPing().
+#if defined(RLZ_NETWORK_IMPLEMENTATION_WIN_INET) && \
+ defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
+#error Exactly one of RLZ_NETWORK_IMPLEMENTATION_WIN_INET and \
+ RLZ_NETWORK_IMPLEMENTATION_CHROME_NET should be defined.
+#endif
+#if !defined(RLZ_NETWORK_IMPLEMENTATION_WIN_INET) && \
+ !defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
+#if defined(OS_WIN)
+#define RLZ_NETWORK_IMPLEMENTATION_WIN_INET
+#else
+#define RLZ_NETWORK_IMPLEMENTATION_CHROME_NET
+#endif
+#endif
+
+#if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
+namespace net {
+class URLRequestContextGetter;
+} // namespace net
+#endif
+
+namespace rlz_lib {
+
+class ScopedRlzValueStoreLock;
+
+// The maximum length of an access points RLZ in bytes.
+const size_t kMaxRlzLength = 64;
+// The maximum length of an access points RLZ in bytes.
+const size_t kMaxDccLength = 128;
+// The maximum length of a CGI string in bytes.
+const size_t kMaxCgiLength = 2048;
+// The maximum length of a ping response we will parse in bytes. If the response
+// is bigger, please break it up into separate calls.
+const size_t kMaxPingResponseLength = 0x4000; // 16K
+
+#if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
+// Set the URLRequestContextGetter used by SendFinancialPing(). The IO message
+// loop returned by this context will be used for the IO done by
+// SendFinancialPing().
+bool RLZ_LIB_API SetURLRequestContext(net::URLRequestContextGetter* context);
+#endif
+
+// RLZ storage functions.
+
+// Get all the events reported by this product as a CGI string to append to
+// the daily ping.
+// Access: HKCU read.
+bool RLZ_LIB_API GetProductEventsAsCgi(Product product, char* unescaped_cgi,
+ size_t unescaped_cgi_size);
+
+// Records an RLZ event.
+// Some events can be product-independent (e.g: First search from home page),
+// and some can be access point independent (e.g. Pack installed). However,
+// product independent events must still include the product which cares about
+// that information being reported.
+// Access: HKCU write.
+bool RLZ_LIB_API RecordProductEvent(Product product, AccessPoint point,
+ Event event_id);
+
+// Clear an event reported by this product. This should be called after a
+// successful ping to the RLZ server.
+// Access: HKCU write.
+bool RLZ_LIB_API ClearProductEvent(Product product, AccessPoint point,
+ Event event_id);
+
+// Clear all reported events and recorded stateful events of this product.
+// This should be called on complete uninstallation of the product.
+// Access: HKCU write.
+bool RLZ_LIB_API ClearAllProductEvents(Product product);
+
+// Clears all product-specifc state from the RLZ registry.
+// Should be called during product uninstallation.
+// This removes outstanding product events, product financial ping times,
+// the product RLS argument (if any), and any RLZ's for access points being
+// uninstalled with the product.
+// access_points is an array terminated with NO_ACCESS_POINT.
+// IMPORTANT: These are the access_points the product is removing as part
+// of the uninstallation, not necessarily all the access points passed to
+// SendFinancialPing() and GetPingParams().
+// access_points can be NULL if no points are being uninstalled.
+// No return value - this is best effort. Will assert in debug mode on
+// failed attempts.
+// Access: HKCU write.
+void RLZ_LIB_API ClearProductState(Product product,
+ const AccessPoint* access_points);
+
+// Get the RLZ value of the access point. If the access point is not Google, the
+// RLZ will be the empty string and the function will return false.
+// Access: HKCU read.
+bool RLZ_LIB_API GetAccessPointRlz(AccessPoint point, char* rlz,
+ size_t rlz_size);
+
+// Set the RLZ for the access-point. Fails and asserts if called when the access
+// point is not set to Google.
+// new_rlz should come from a server-response. Client applications should not
+// create their own RLZ values.
+// Access: HKCU write.
+bool RLZ_LIB_API SetAccessPointRlz(AccessPoint point, const char* new_rlz);
+
+// Financial Server pinging functions.
+// These functions deal with pinging the RLZ financial server and parsing and
+// acting upon the response. Clients should SendFinancialPing() to avoid needing
+// these functions. However, these functions allow clients to split the various
+// parts of the pinging process up as needed (to avoid firewalls, etc).
+
+// Forms the HTTP request to send to the RLZ financial server.
+//
+// product : The product to ping for.
+// access_points : The access points this product affects. Array must be
+// terminated with NO_ACCESS_POINT.
+// product_signature : The signature sent with daily pings (e.g. swg, ietb)
+// product_brand : The brand of the pinging product, if any.
+// product_id : The product-specific installation ID (can be NULL).
+// product_lang : The language for the product (used to determine cohort).
+// exclude_machine_id : Whether the Machine ID should be explicitly excluded
+// based on the products privacy policy.
+// request : The buffer where the function returns the HTTP request.
+// request_buffer_size: The size of the request buffer in bytes. The buffer
+// size (kMaxCgiLength+1) is guaranteed to be enough.
+//
+// Access: HKCU read.
+bool RLZ_LIB_API FormFinancialPingRequest(Product product,
+ const AccessPoint* access_points,
+ const char* product_signature,
+ const char* product_brand,
+ const char* product_id,
+ const char* product_lang,
+ bool exclude_machine_id,
+ char* request,
+ size_t request_buffer_size);
+
+// Pings the financial server and returns the HTTP response. This will fail
+// if it is too early to ping the server since the last ping.
+//
+// If RLZ_NETWORK_IMPLEMENTATION_CHROME_NET is set, SetURLRequestContext() needs
+// to be called before calling this function.
+//
+// product : The product to ping for.
+// request : The HTTP request (for example, returned by
+// FormFinancialPingRequest).
+// response : The buffer in which the HTTP response is returned.
+// response_buffer_size : The size of the response buffer in bytes. The buffer
+// size (kMaxPingResponseLength+1) is enough for all
+// legitimate server responses (any response that is
+// bigger should be considered the same way as a general
+// network problem).
+//
+// Access: HKCU read.
+bool RLZ_LIB_API PingFinancialServer(Product product,
+ const char* request,
+ char* response,
+ size_t response_buffer_size);
+
+// Checks if a ping response is valid - ie. it has a checksum line which
+// is the CRC-32 checksum of the message uptil the checksum. If
+// checksum_idx is not NULL, it will get the index of the checksum, i.e. -
+// the effective end of the message.
+// Access: No restrictions.
+bool RLZ_LIB_API IsPingResponseValid(const char* response,
+ int* checksum_idx);
+
+
+// Complex helpers built on top of other functions.
+
+// Parses the responses from the financial server and updates product state
+// and access point RLZ's in registry. Like ParsePingResponse(), but also
+// updates the last ping time.
+// Access: HKCU write.
+bool RLZ_LIB_API ParseFinancialPingResponse(Product product,
+ const char* response);
+
+// Send the ping with RLZs and events to the PSO server.
+// This ping method should be called daily. (More frequent calls will fail).
+// Also, if there are no events, the call will succeed only once a week.
+//
+// If RLZ_NETWORK_IMPLEMENTATION_CHROME_NET is set, SetURLRequestContext() needs
+// to be called before calling this function.
+//
+// product : The product to ping for.
+// access_points : The access points this product affects. Array must be
+// terminated with NO_ACCESS_POINT.
+// product_signature : The signature sent with daily pings (e.g. swg, ietb)
+// product_brand : The brand of the pinging product, if any.
+// product_id : The product-specific installation ID (can be NULL).
+// product_lang : The language for the product (used to determine cohort).
+// exclude_machine_id : Whether the Machine ID should be explicitly excluded
+// based on the products privacy policy.
+//
+// Returns true on successful ping and response, false otherwise.
+// Access: HKCU write.
+bool RLZ_LIB_API SendFinancialPing(Product product,
+ const AccessPoint* access_points,
+ const char* product_signature,
+ const char* product_brand,
+ const char* product_id,
+ const char* product_lang,
+ bool exclude_machine_id);
+
+// An alternate implementations of SendFinancialPing with the same behavior,
+// except the caller can optionally choose to skip the timing check.
+bool RLZ_LIB_API SendFinancialPing(Product product,
+ const AccessPoint* access_points,
+ const char* product_signature,
+ const char* product_brand,
+ const char* product_id,
+ const char* product_lang,
+ bool exclude_machine_id,
+ const bool skip_time_check);
+
+// Parses RLZ related ping response information from the server.
+// Updates stored RLZ values and clears stored events accordingly.
+// Access: HKCU write.
+bool RLZ_LIB_API ParsePingResponse(Product product, const char* response);
+
+
+// Copies the events associated with the product and the RLZ's for each access
+// point in access_points into cgi. This string can be directly appended
+// to a ping (will need an & if not first paramter).
+// access_points must be an array of AccessPoints terminated with
+// NO_ACCESS_POINT.
+// Access: HKCU read.
+bool RLZ_LIB_API GetPingParams(Product product,
+ const AccessPoint* access_points,
+ char* unescaped_cgi, size_t unescaped_cgi_size);
+
+#if defined(OS_WIN)
+// OEM Deal confirmation storage functions. OEM Deals are windows-only.
+
+// Makes the OEM Deal Confirmation code writable by all users on the machine.
+// This should be called before calling SetMachineDealCode from a non-admin
+// account.
+// Access: HKLM write.
+bool RLZ_LIB_API CreateMachineState(void);
+
+// Set the OEM Deal Confirmation Code (DCC). This information is used for RLZ
+// initalization.
+// Access: HKLM write, or
+// HKCU read if rlz_lib::CreateMachineState() has been sucessfully called.
+bool RLZ_LIB_API SetMachineDealCode(const char* dcc);
+
+// Get the DCC cgi argument string to append to a daily ping.
+// Should be used only by OEM deal trackers. Applications should use the
+// GetMachineDealCode method which has an AccessPoint paramter.
+// Access: HKLM read.
+bool RLZ_LIB_API GetMachineDealCodeAsCgi(char* cgi, size_t cgi_size);
+
+// Get the DCC value stored in registry.
+// Should be used only by OEM deal trackers. Applications should use the
+// GetMachineDealCode method which has an AccessPoint paramter.
+// Access: HKLM read.
+bool RLZ_LIB_API GetMachineDealCode(char* dcc, size_t dcc_size);
+
+// Parses a ping response, checks if it is valid and sets the machine DCC
+// from the response. The ping must also contain the current DCC value in
+// order to be considered valid.
+// Access: HKLM write;
+// HKCU write if CreateMachineState() has been successfully called.
+bool RLZ_LIB_API SetMachineDealCodeFromPingResponse(const char* response);
+
+#endif
+
+// Segment RLZ persistence based on branding information.
+// All information for a given product is persisted under keys with the either
+// product's name or its access point's name. This assumes that only
+// one instance of the product is installed on the machine, and that only one
+// product brand is associated with it.
+//
+// In some cases, a given product may be using supplementary brands. The RLZ
+// information must be kept separately for each of these brands. To achieve
+// this segmentation, scope all RLZ library calls that deal with supplementary
+// brands within the lifetime of an rlz_lib::ProductBranding instance.
+//
+// For example, to record events for a supplementary brand, do the following:
+//
+// {
+// rlz_lib::SupplementaryBranding branding("AAAA");
+// // This call to RecordProductEvent is scoped to the AAAA brand.
+// rlz_lib::RecordProductEvent(rlz_lib::DESKTOP, rlz_lib::GD_DESKBAND,
+// rlz_lib::INSTALL);
+// }
+//
+// // This call to RecordProductEvent is not scoped to any supplementary brand.
+// rlz_lib::RecordProductEvent(rlz_lib::DESKTOP, rlz_lib::GD_DESKBAND,
+// rlz_lib::INSTALL);
+//
+// In particular, this affects the recording of stateful events and the sending
+// of financial pings. In the former case, a stateful event recorded while
+// scoped to a supplementary brand will be recorded again when scoped to a
+// different supplementary brand (or not scoped at all). In the latter case,
+// the time skip check is specific to each supplementary brand.
+class SupplementaryBranding {
+ public:
+ SupplementaryBranding(const char* brand);
+ ~SupplementaryBranding();
+
+ static const std::string& GetBrand();
+
+ private:
+ ScopedRlzValueStoreLock* lock_;
+};
+
+} // namespace rlz_lib
+
+#endif // RLZ_LIB_RLZ_LIB_H_
diff --git a/chromium/rlz/lib/rlz_lib_clear.cc b/chromium/rlz/lib/rlz_lib_clear.cc
new file mode 100644
index 00000000000..0dd13a40ab8
--- /dev/null
+++ b/chromium/rlz/lib/rlz_lib_clear.cc
@@ -0,0 +1,82 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The methods in this file belong conceptually to rlz_lib.cc. However, some
+// programs depend on rlz only to call ClearAllProductEvents(), so this file
+// contains this in fairly self-contained form to make it easier for linkers
+// to strip away most of rlz. In particular, this file should not reference any
+// symbols defined in financial_ping.cc.
+
+#include "rlz/lib/rlz_lib.h"
+
+#include "base/lazy_instance.h"
+#include "rlz/lib/assert.h"
+#include "rlz/lib/rlz_value_store.h"
+
+namespace rlz_lib {
+
+bool ClearAllProductEvents(Product product) {
+ rlz_lib::ScopedRlzValueStoreLock lock;
+ rlz_lib::RlzValueStore* store = lock.GetStore();
+ if (!store || !store->HasAccess(rlz_lib::RlzValueStore::kWriteAccess))
+ return false;
+
+ bool result;
+ result = store->ClearAllProductEvents(product);
+ result &= store->ClearAllStatefulEvents(product);
+ return result;
+}
+
+void ClearProductState(Product product, const AccessPoint* access_points) {
+ rlz_lib::ScopedRlzValueStoreLock lock;
+ rlz_lib::RlzValueStore* store = lock.GetStore();
+ if (!store || !store->HasAccess(rlz_lib::RlzValueStore::kWriteAccess))
+ return;
+
+ // Delete all product specific state.
+ VERIFY(ClearAllProductEvents(product));
+ VERIFY(store->ClearPingTime(product));
+
+ // Delete all RLZ's for access points being uninstalled.
+ if (access_points) {
+ for (int i = 0; access_points[i] != NO_ACCESS_POINT; i++) {
+ VERIFY(store->ClearAccessPointRlz(access_points[i]));
+ }
+ }
+
+ store->CollectGarbage();
+}
+
+static base::LazyInstance<std::string>::Leaky g_supplemental_branding;
+
+SupplementaryBranding::SupplementaryBranding(const char* brand)
+ : lock_(new ScopedRlzValueStoreLock) {
+ if (!lock_->GetStore())
+ return;
+
+ if (!g_supplemental_branding.Get().empty()) {
+ ASSERT_STRING("ProductBranding: existing brand is not empty");
+ return;
+ }
+
+ if (brand == NULL || brand[0] == 0) {
+ ASSERT_STRING("ProductBranding: new brand is empty");
+ return;
+ }
+
+ g_supplemental_branding.Get() = brand;
+}
+
+SupplementaryBranding::~SupplementaryBranding() {
+ if (lock_->GetStore())
+ g_supplemental_branding.Get().clear();
+ delete lock_;
+}
+
+// static
+const std::string& SupplementaryBranding::GetBrand() {
+ return g_supplemental_branding.Get();
+}
+
+} // namespace rlz_lib
diff --git a/chromium/rlz/lib/rlz_lib_test.cc b/chromium/rlz/lib/rlz_lib_test.cc
new file mode 100644
index 00000000000..f21c5400698
--- /dev/null
+++ b/chromium/rlz/lib/rlz_lib_test.cc
@@ -0,0 +1,892 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A test application for the RLZ library.
+//
+// These tests should not be executed on the build server:
+// - They assert for the failed cases.
+// - They modify machine state (registry).
+//
+// These tests require write access to HKLM and HKCU.
+//
+// The "GGLA" brand is used to test the normal code flow of the code, and the
+// "TEST" brand is used to test the supplementary brand code code flow.
+
+#include "base/posix/eintr_wrapper.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "rlz/lib/rlz_lib.h"
+#include "rlz/lib/rlz_value_store.h"
+#include "rlz/test/rlz_test_helpers.h"
+
+#if defined(OS_WIN)
+#include <Windows.h>
+#include "rlz/win/lib/machine_deal.h"
+#endif
+
+#if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
+#include "base/mac/scoped_nsautorelease_pool.h"
+#include "base/threading/thread.h"
+#include "net/url_request/url_request_test_util.h"
+#endif
+
+
+class MachineDealCodeHelper
+#if defined(OS_WIN)
+ : public rlz_lib::MachineDealCode
+#endif
+ {
+ public:
+ static bool Clear() {
+#if defined(OS_WIN)
+ return rlz_lib::MachineDealCode::Clear();
+#else
+ return true;
+#endif
+ }
+
+ private:
+ MachineDealCodeHelper() {}
+ ~MachineDealCodeHelper() {}
+};
+
+class RlzLibTest : public RlzLibTestBase {
+};
+
+TEST_F(RlzLibTest, RecordProductEvent) {
+ char cgi_50[50];
+
+ EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+ EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ cgi_50, 50));
+ EXPECT_STREQ("events=I7S", cgi_50);
+
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
+ EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ cgi_50, 50));
+ EXPECT_STREQ("events=I7S,W1I", cgi_50);
+
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+ EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ cgi_50, 50));
+ EXPECT_STREQ("events=I7S,W1I", cgi_50);
+}
+
+TEST_F(RlzLibTest, ClearProductEvent) {
+ char cgi_50[50];
+
+ // Clear 1 of 1 events.
+ EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+ EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ cgi_50, 50));
+ EXPECT_STREQ("events=I7S", cgi_50);
+ EXPECT_TRUE(rlz_lib::ClearProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+ EXPECT_FALSE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ cgi_50, 50));
+ EXPECT_STREQ("", cgi_50);
+
+ // Clear 1 of 2 events.
+ EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
+ EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ cgi_50, 50));
+ EXPECT_STREQ("events=I7S,W1I", cgi_50);
+ EXPECT_TRUE(rlz_lib::ClearProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+ EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ cgi_50, 50));
+ EXPECT_STREQ("events=W1I", cgi_50);
+
+ // Clear a non-recorded event.
+ EXPECT_TRUE(rlz_lib::ClearProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IETB_SEARCH_BOX, rlz_lib::FIRST_SEARCH));
+ EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ cgi_50, 50));
+ EXPECT_STREQ("events=W1I", cgi_50);
+}
+
+
+TEST_F(RlzLibTest, GetProductEventsAsCgi) {
+ char cgi_50[50];
+ char cgi_1[1];
+
+ EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+ EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ cgi_50, 50));
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
+
+ EXPECT_FALSE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ cgi_1, 1));
+ EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ cgi_50, 50));
+ EXPECT_STREQ("events=I7S,W1I", cgi_50);
+}
+
+TEST_F(RlzLibTest, ClearAllAllProductEvents) {
+ char cgi_50[50];
+
+ EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+ EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ cgi_50, 50));
+ EXPECT_STREQ("events=I7S", cgi_50);
+
+ EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
+ EXPECT_FALSE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ cgi_50, 50));
+ EXPECT_STREQ("", cgi_50);
+}
+
+TEST_F(RlzLibTest, SetAccessPointRlz) {
+ char rlz_50[50];
+ EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, ""));
+ EXPECT_TRUE(rlz_lib::GetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, rlz_50, 50));
+ EXPECT_STREQ("", rlz_50);
+
+ EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, "IeTbRlz"));
+ EXPECT_TRUE(rlz_lib::GetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, rlz_50, 50));
+ EXPECT_STREQ("IeTbRlz", rlz_50);
+}
+
+TEST_F(RlzLibTest, GetAccessPointRlz) {
+ char rlz_1[1];
+ char rlz_50[50];
+ EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, ""));
+ EXPECT_TRUE(rlz_lib::GetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, rlz_1, 1));
+ EXPECT_STREQ("", rlz_1);
+
+ EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, "IeTbRlz"));
+ EXPECT_FALSE(rlz_lib::GetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, rlz_1, 1));
+ EXPECT_TRUE(rlz_lib::GetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, rlz_50, 50));
+ EXPECT_STREQ("IeTbRlz", rlz_50);
+}
+
+TEST_F(RlzLibTest, GetPingParams) {
+ MachineDealCodeHelper::Clear();
+
+ EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX,
+ "TbRlzValue"));
+ EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IE_HOME_PAGE, ""));
+
+ char cgi[2048];
+ rlz_lib::AccessPoint points[] =
+ {rlz_lib::IETB_SEARCH_BOX, rlz_lib::NO_ACCESS_POINT,
+ rlz_lib::NO_ACCESS_POINT};
+
+ EXPECT_TRUE(rlz_lib::GetPingParams(rlz_lib::TOOLBAR_NOTIFIER, points,
+ cgi, 2048));
+ EXPECT_STREQ("rep=2&rlz=T4:TbRlzValue", cgi);
+
+#if defined(OS_WIN)
+ EXPECT_TRUE(rlz_lib::MachineDealCode::Set("dcc_value"));
+#define DCC_PARAM "&dcc=dcc_value"
+#else
+#define DCC_PARAM ""
+#endif
+
+ EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, ""));
+ EXPECT_TRUE(rlz_lib::GetPingParams(rlz_lib::TOOLBAR_NOTIFIER, points,
+ cgi, 2048));
+ EXPECT_STREQ("rep=2&rlz=T4:" DCC_PARAM, cgi);
+
+ EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX,
+ "TbRlzValue"));
+ EXPECT_FALSE(rlz_lib::GetPingParams(rlz_lib::TOOLBAR_NOTIFIER, points,
+ cgi, 23 + strlen(DCC_PARAM)));
+ EXPECT_STREQ("", cgi);
+ EXPECT_TRUE(rlz_lib::GetPingParams(rlz_lib::TOOLBAR_NOTIFIER, points,
+ cgi, 24 + strlen(DCC_PARAM)));
+ EXPECT_STREQ("rep=2&rlz=T4:TbRlzValue" DCC_PARAM, cgi);
+
+ EXPECT_TRUE(GetAccessPointRlz(rlz_lib::IE_HOME_PAGE, cgi, 2048));
+ points[2] = rlz_lib::IE_HOME_PAGE;
+ EXPECT_TRUE(rlz_lib::GetPingParams(rlz_lib::TOOLBAR_NOTIFIER, points,
+ cgi, 2048));
+ EXPECT_STREQ("rep=2&rlz=T4:TbRlzValue" DCC_PARAM, cgi);
+}
+
+TEST_F(RlzLibTest, IsPingResponseValid) {
+ const char* kBadPingResponses[] = {
+ // No checksum.
+ "version: 3.0.914.7250\r\n"
+ "url: http://www.corp.google.com/~av/45/opt/SearchWithGoogleUpdate.exe\r\n"
+ "launch-action: custom-action\r\n"
+ "launch-target: SearchWithGoogleUpdate.exe\r\n"
+ "signature: c08a3f4438e1442c4fe5678ee147cf6c5516e5d62bb64e\r\n"
+ "rlz: 1R1_____en__252\r\n"
+ "rlzXX: 1R1_____en__250\r\n",
+
+ // Invalid checksum.
+ "version: 3.0.914.7250\r\n"
+ "url: http://www.corp.google.com/~av/45/opt/SearchWithGoogleUpdate.exe\r\n"
+ "launch-action: custom-action\r\n"
+ "launch-target: SearchWithGoogleUpdate.exe\r\n"
+ "signature: c08a3f4438e1442c4fe5678ee147cf6c5516e5d62bb64e\r\n"
+ "rlz: 1R1_____en__252\r\n"
+ "rlzXX: 1R1_____en__250\r\n"
+ "rlzT4 1T4_____en__251\r\n"
+ "rlzT4: 1T4_____en__252\r\n"
+ "rlz\r\n"
+ "crc32: B12CC79A",
+
+ // Misplaced checksum.
+ "version: 3.0.914.7250\r\n"
+ "url: http://www.corp.google.com/~av/45/opt/SearchWithGoogleUpdate.exe\r\n"
+ "launch-action: custom-action\r\n"
+ "launch-target: SearchWithGoogleUpdate.exe\r\n"
+ "signature: c08a3f4438e1442c4fe5678ee147cf6c5516e5d62bb64e\r\n"
+ "rlz: 1R1_____en__252\r\n"
+ "rlzXX: 1R1_____en__250\r\n"
+ "crc32: B12CC79C\r\n"
+ "rlzT4 1T4_____en__251\r\n"
+ "rlzT4: 1T4_____en__252\r\n"
+ "rlz\r\n",
+
+ NULL
+ };
+
+ const char* kGoodPingResponses[] = {
+ "version: 3.0.914.7250\r\n"
+ "url: http://www.corp.google.com/~av/45/opt/SearchWithGoogleUpdate.exe\r\n"
+ "launch-action: custom-action\r\n"
+ "launch-target: SearchWithGoogleUpdate.exe\r\n"
+ "signature: c08a3f4438e1442c4fe5678ee147cf6c5516e5d62bb64e\r\n"
+ "rlz: 1R1_____en__252\r\n"
+ "rlzXX: 1R1_____en__250\r\n"
+ "rlzT4 1T4_____en__251\r\n"
+ "rlzT4: 1T4_____en__252\r\n"
+ "rlz\r\n"
+ "crc32: D6FD55A3",
+
+ "version: 3.0.914.7250\r\n"
+ "url: http://www.corp.google.com/~av/45/opt/SearchWithGoogleUpdate.exe\r\n"
+ "launch-action: custom-action\r\n"
+ "launch-target: SearchWithGoogleUpdate.exe\r\n"
+ "signature: c08a3f4438e1442c4fe5678ee147cf6c5516e5d62bb64e\r\n"
+ "rlz: 1R1_____en__252\r\n"
+ "rlzXX: 1R1_____en__250\r\n"
+ "rlzT4 1T4_____en__251\r\n"
+ "rlzT4: 1T4_____en__252\r\n"
+ "rlz\r\n"
+ "crc32: D6FD55A3\r\n"
+ "extradata: not checksummed",
+
+ NULL
+ };
+
+ for (int i = 0; kBadPingResponses[i]; i++)
+ EXPECT_FALSE(rlz_lib::IsPingResponseValid(kBadPingResponses[i], NULL));
+
+ for (int i = 0; kGoodPingResponses[i]; i++)
+ EXPECT_TRUE(rlz_lib::IsPingResponseValid(kGoodPingResponses[i], NULL));
+}
+
+TEST_F(RlzLibTest, ParsePingResponse) {
+ const char* kPingResponse =
+ "version: 3.0.914.7250\r\n"
+ "url: http://www.corp.google.com/~av/45/opt/SearchWithGoogleUpdate.exe\r\n"
+ "launch-action: custom-action\r\n"
+ "launch-target: SearchWithGoogleUpdate.exe\r\n"
+ "signature: c08a3f4438e1442c4fe5678ee147cf6c5516e5d62bb64e\r\n"
+ "rlz: 1R1_____en__252\r\n" // Invalid RLZ - no access point.
+ "rlzXX: 1R1_____en__250\r\n" // Invalid RLZ - bad access point.
+ "rlzT4 1T4_____en__251\r\n" // Invalid RLZ - missing colon.
+ "rlzT4: 1T4_____en__252\r\n" // GoodRLZ.
+ "events: I7S,W1I\r\n" // Clear all events.
+ "rlz\r\n"
+ "dcc: dcc_value\r\n"
+ "crc32: F9070F81";
+
+#if defined(OS_WIN)
+ EXPECT_TRUE(rlz_lib::MachineDealCode::Set("dcc_value2"));
+#endif
+
+ // Record some product events to check that they get cleared.
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
+
+ EXPECT_TRUE(rlz_lib::SetAccessPointRlz(
+ rlz_lib::IETB_SEARCH_BOX, "TbRlzValue"));
+
+ EXPECT_TRUE(rlz_lib::ParsePingResponse(rlz_lib::TOOLBAR_NOTIFIER,
+ kPingResponse));
+
+#if defined(OS_WIN)
+ EXPECT_TRUE(rlz_lib::MachineDealCode::Set("dcc_value"));
+#endif
+ EXPECT_TRUE(rlz_lib::ParsePingResponse(rlz_lib::TOOLBAR_NOTIFIER,
+ kPingResponse));
+
+ char value[50];
+ EXPECT_TRUE(rlz_lib::GetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, value, 50));
+ EXPECT_STREQ("1T4_____en__252", value);
+ EXPECT_FALSE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ value, 50));
+ EXPECT_STREQ("", value);
+
+ const char* kPingResponse2 =
+ "rlzT4: 1T4_____de__253 \r\n" // Good with extra spaces.
+ "crc32: 321334F5\r\n";
+ EXPECT_TRUE(rlz_lib::ParsePingResponse(rlz_lib::TOOLBAR_NOTIFIER,
+ kPingResponse2));
+ EXPECT_TRUE(rlz_lib::GetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, value, 50));
+ EXPECT_STREQ("1T4_____de__253", value);
+
+ const char* kPingResponse3 =
+ "crc32: 0\r\n"; // Good RLZ - empty response.
+ EXPECT_TRUE(rlz_lib::ParsePingResponse(rlz_lib::TOOLBAR_NOTIFIER,
+ kPingResponse3));
+ EXPECT_STREQ("1T4_____de__253", value);
+}
+
+// Test whether a stateful event will only be sent in financial pings once.
+TEST_F(RlzLibTest, ParsePingResponseWithStatefulEvents) {
+ const char* kPingResponse =
+ "version: 3.0.914.7250\r\n"
+ "url: http://www.corp.google.com/~av/45/opt/SearchWithGoogleUpdate.exe\r\n"
+ "launch-action: custom-action\r\n"
+ "launch-target: SearchWithGoogleUpdate.exe\r\n"
+ "signature: c08a3f4438e1442c4fe5678ee147cf6c5516e5d62bb64e\r\n"
+ "rlzT4: 1T4_____en__252\r\n" // GoodRLZ.
+ "events: I7S,W1I\r\n" // Clear all events.
+ "stateful-events: W1I\r\n" // W1I as an stateful event.
+ "rlz\r\n"
+ "dcc: dcc_value\r\n"
+ "crc32: 55191759";
+
+ EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
+
+ // Record some product events to check that they get cleared.
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
+
+ EXPECT_TRUE(rlz_lib::SetAccessPointRlz(
+ rlz_lib::IETB_SEARCH_BOX, "TbRlzValue"));
+
+ EXPECT_TRUE(rlz_lib::ParsePingResponse(rlz_lib::TOOLBAR_NOTIFIER,
+ kPingResponse));
+
+ // Check all the events sent earlier are cleared.
+ char value[50];
+ EXPECT_FALSE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ value, 50));
+ EXPECT_STREQ("", value);
+
+ // Record both events (one is stateless and the other is stateful) again.
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
+
+ // Check the stateful event won't be sent again while the stateless one will.
+ EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ value, 50));
+ EXPECT_STREQ("events=I7S", value);
+
+ // Test that stateful events are cleared by ClearAllProductEvents(). After
+ // calling it, trying to record a stateful again should result in it being
+ // recorded again.
+ EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
+ EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ value, 50));
+ EXPECT_STREQ("events=W1I", value);
+}
+
+TEST_F(RlzLibTest, SendFinancialPing) {
+ // We don't really check a value or result in this test. All this does is
+ // attempt to ping the financial server, which you can verify in Fiddler.
+ // TODO: Make this a measurable test.
+
+#if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
+#if defined(OS_MACOSX)
+ base::mac::ScopedNSAutoreleasePool pool;
+#endif
+
+ base::Thread::Options options;
+ options.message_loop_type = base::MessageLoop::TYPE_IO;
+
+ base::Thread io_thread("rlz_unittest_io_thread");
+ ASSERT_TRUE(io_thread.StartWithOptions(options));
+
+ scoped_refptr<net::TestURLRequestContextGetter> context =
+ new net::TestURLRequestContextGetter(
+ io_thread.message_loop()->message_loop_proxy());
+ rlz_lib::SetURLRequestContext(context.get());
+
+ class URLRequestRAII {
+ public:
+ URLRequestRAII(net::URLRequestContextGetter* context) {
+ rlz_lib::SetURLRequestContext(context);
+ }
+ ~URLRequestRAII() {
+ rlz_lib::SetURLRequestContext(NULL);
+ }
+ };
+
+ URLRequestRAII set_context(context.get());
+#endif
+
+ MachineDealCodeHelper::Clear();
+#if defined(OS_WIN)
+ EXPECT_TRUE(rlz_lib::MachineDealCode::Set("dcc_value"));
+#endif
+
+ EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX,
+ "TbRlzValue"));
+
+ EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
+
+ rlz_lib::AccessPoint points[] =
+ {rlz_lib::IETB_SEARCH_BOX, rlz_lib::NO_ACCESS_POINT,
+ rlz_lib::NO_ACCESS_POINT};
+
+ std::string request;
+ rlz_lib::SendFinancialPing(rlz_lib::TOOLBAR_NOTIFIER, points,
+ "swg", "GGLA", "SwgProductId1234", "en-UK", false,
+ /*skip_time_check=*/true);
+}
+
+TEST_F(RlzLibTest, ClearProductState) {
+ MachineDealCodeHelper::Clear();
+
+ EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX,
+ "TbRlzValue"));
+ EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::GD_DESKBAND,
+ "GdbRlzValue"));
+
+ rlz_lib::AccessPoint points[] =
+ { rlz_lib::IETB_SEARCH_BOX, rlz_lib::NO_ACCESS_POINT };
+
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IETB_SEARCH_BOX, rlz_lib::INSTALL));
+
+ rlz_lib::AccessPoint points2[] =
+ { rlz_lib::IETB_SEARCH_BOX,
+ rlz_lib::GD_DESKBAND,
+ rlz_lib::NO_ACCESS_POINT };
+
+ char cgi[2048];
+ EXPECT_TRUE(rlz_lib::GetPingParams(rlz_lib::TOOLBAR_NOTIFIER, points2,
+ cgi, 2048));
+ EXPECT_STREQ("rep=2&rlz=T4:TbRlzValue,D1:GdbRlzValue", cgi);
+
+ EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ cgi, 2048));
+ std::string events(cgi);
+ EXPECT_LT(0u, events.find("I7S"));
+ EXPECT_LT(0u, events.find("T4I"));
+ EXPECT_LT(0u, events.find("T4R"));
+
+ rlz_lib::ClearProductState(rlz_lib::TOOLBAR_NOTIFIER, points);
+
+ EXPECT_TRUE(rlz_lib::GetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX,
+ cgi, 2048));
+ EXPECT_STREQ("", cgi);
+ EXPECT_TRUE(rlz_lib::GetAccessPointRlz(rlz_lib::GD_DESKBAND,
+ cgi, 2048));
+ EXPECT_STREQ("GdbRlzValue", cgi);
+
+ EXPECT_FALSE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ cgi, 2048));
+ EXPECT_STREQ("", cgi);
+}
+
+#if defined(OS_WIN)
+template<class T>
+class typed_buffer_ptr {
+ scoped_ptr<char[]> buffer_;
+
+ public:
+ typed_buffer_ptr() {
+ }
+
+ explicit typed_buffer_ptr(size_t size) : buffer_(new char[size]) {
+ }
+
+ void reset(size_t size) {
+ buffer_.reset(new char[size]);
+ }
+
+ operator T*() {
+ return reinterpret_cast<T*>(buffer_.get());
+ }
+};
+
+namespace rlz_lib {
+bool HasAccess(PSID sid, ACCESS_MASK access_mask, ACL* dacl);
+}
+
+bool EmptyAcl(ACL* acl) {
+ ACL_SIZE_INFORMATION info;
+ bool ret = GetAclInformation(acl, &info, sizeof(info), AclSizeInformation);
+ EXPECT_TRUE(ret);
+
+ for (DWORD i = 0; i < info.AceCount && ret; ++i) {
+ ret = DeleteAce(acl, 0);
+ EXPECT_TRUE(ret);
+ }
+
+ return ret;
+}
+
+TEST_F(RlzLibTest, HasAccess) {
+ // Create a SID that represents ALL USERS.
+ DWORD users_sid_size = SECURITY_MAX_SID_SIZE;
+ typed_buffer_ptr<SID> users_sid(users_sid_size);
+ CreateWellKnownSid(WinBuiltinUsersSid, NULL, users_sid, &users_sid_size);
+
+ // RLZ always asks for KEY_ALL_ACCESS access to the key. This is what we
+ // test here.
+
+ // No ACL mean no access.
+ EXPECT_FALSE(rlz_lib::HasAccess(users_sid, KEY_ALL_ACCESS, NULL));
+
+ // Create an ACL for these tests.
+ const DWORD kMaxAclSize = 1024;
+ typed_buffer_ptr<ACL> dacl(kMaxAclSize);
+ InitializeAcl(dacl, kMaxAclSize, ACL_REVISION);
+
+ // Empty DACL mean no access.
+ EXPECT_FALSE(rlz_lib::HasAccess(users_sid, KEY_ALL_ACCESS, dacl));
+
+ // ACE without all needed privileges should mean no access.
+ EXPECT_TRUE(AddAccessAllowedAce(dacl, ACL_REVISION, KEY_READ, users_sid));
+ EXPECT_FALSE(rlz_lib::HasAccess(users_sid, KEY_ALL_ACCESS, dacl));
+
+ // ACE without all needed privileges should mean no access.
+ EXPECT_TRUE(EmptyAcl(dacl));
+ EXPECT_TRUE(AddAccessAllowedAce(dacl, ACL_REVISION, KEY_WRITE, users_sid));
+ EXPECT_FALSE(rlz_lib::HasAccess(users_sid, KEY_ALL_ACCESS, dacl));
+
+ // A deny ACE before an allow ACE should not give access.
+ EXPECT_TRUE(EmptyAcl(dacl));
+ EXPECT_TRUE(AddAccessDeniedAce(dacl, ACL_REVISION, KEY_ALL_ACCESS,
+ users_sid));
+ EXPECT_TRUE(AddAccessAllowedAce(dacl, ACL_REVISION, KEY_ALL_ACCESS,
+ users_sid));
+ EXPECT_FALSE(rlz_lib::HasAccess(users_sid, KEY_ALL_ACCESS, dacl));
+
+ // A deny ACE before an allow ACE should not give access.
+ EXPECT_TRUE(EmptyAcl(dacl));
+ EXPECT_TRUE(AddAccessDeniedAce(dacl, ACL_REVISION, KEY_READ, users_sid));
+ EXPECT_TRUE(AddAccessAllowedAce(dacl, ACL_REVISION, KEY_ALL_ACCESS,
+ users_sid));
+ EXPECT_FALSE(rlz_lib::HasAccess(users_sid, KEY_ALL_ACCESS, dacl));
+
+
+ // An allow ACE without all required bits should not give access.
+ EXPECT_TRUE(EmptyAcl(dacl));
+ EXPECT_TRUE(AddAccessAllowedAce(dacl, ACL_REVISION, KEY_WRITE, users_sid));
+ EXPECT_FALSE(rlz_lib::HasAccess(users_sid, KEY_ALL_ACCESS, dacl));
+
+ // An allow ACE with all required bits should give access.
+ EXPECT_TRUE(EmptyAcl(dacl));
+ EXPECT_TRUE(AddAccessAllowedAce(dacl, ACL_REVISION, KEY_ALL_ACCESS,
+ users_sid));
+ EXPECT_TRUE(rlz_lib::HasAccess(users_sid, KEY_ALL_ACCESS, dacl));
+
+ // A deny ACE after an allow ACE should not give access.
+ EXPECT_TRUE(EmptyAcl(dacl));
+ EXPECT_TRUE(AddAccessAllowedAce(dacl, ACL_REVISION, KEY_ALL_ACCESS,
+ users_sid));
+ EXPECT_TRUE(AddAccessDeniedAce(dacl, ACL_REVISION, KEY_READ, users_sid));
+ EXPECT_TRUE(rlz_lib::HasAccess(users_sid, KEY_ALL_ACCESS, dacl));
+
+ // An inherit-only allow ACE should not give access.
+ EXPECT_TRUE(EmptyAcl(dacl));
+ EXPECT_TRUE(AddAccessAllowedAceEx(dacl, ACL_REVISION, INHERIT_ONLY_ACE,
+ KEY_ALL_ACCESS, users_sid));
+ EXPECT_FALSE(rlz_lib::HasAccess(users_sid, KEY_ALL_ACCESS, dacl));
+
+ // An inherit-only deny ACE should not apply.
+ EXPECT_TRUE(EmptyAcl(dacl));
+ EXPECT_TRUE(AddAccessDeniedAceEx(dacl, ACL_REVISION, INHERIT_ONLY_ACE,
+ KEY_ALL_ACCESS, users_sid));
+ EXPECT_TRUE(AddAccessAllowedAce(dacl, ACL_REVISION, KEY_ALL_ACCESS,
+ users_sid));
+ EXPECT_TRUE(rlz_lib::HasAccess(users_sid, KEY_ALL_ACCESS, dacl));
+}
+#endif
+
+TEST_F(RlzLibTest, BrandingRecordProductEvent) {
+ // Don't run these tests if a supplementary brand is already in place. That
+ // way we can control the branding.
+ if (!rlz_lib::SupplementaryBranding::GetBrand().empty())
+ return;
+
+ char cgi_50[50];
+
+ // Record different events for the same product with diffrent branding, and
+ // make sure that the information remains separate.
+ EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
+ {
+ rlz_lib::SupplementaryBranding branding("TEST");
+ EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
+ }
+
+ // Test that recording events with the default brand and a supplementary
+ // brand don't overwrite each other.
+
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+ EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ cgi_50, 50));
+ EXPECT_STREQ("events=I7S", cgi_50);
+
+ {
+ rlz_lib::SupplementaryBranding branding("TEST");
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::INSTALL));
+ EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ cgi_50, 50));
+ EXPECT_STREQ("events=I7I", cgi_50);
+ }
+
+ EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ cgi_50, 50));
+ EXPECT_STREQ("events=I7S", cgi_50);
+}
+
+TEST_F(RlzLibTest, BrandingSetAccessPointRlz) {
+ // Don't run these tests if a supplementary brand is already in place. That
+ // way we can control the branding.
+ if (!rlz_lib::SupplementaryBranding::GetBrand().empty())
+ return;
+
+ char rlz_50[50];
+
+ // Test that setting RLZ strings with the default brand and a supplementary
+ // brand don't overwrite each other.
+
+ EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, "IeTbRlz"));
+ EXPECT_TRUE(rlz_lib::GetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, rlz_50, 50));
+ EXPECT_STREQ("IeTbRlz", rlz_50);
+
+ {
+ rlz_lib::SupplementaryBranding branding("TEST");
+
+ EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, "SuppRlz"));
+ EXPECT_TRUE(rlz_lib::GetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, rlz_50,
+ 50));
+ EXPECT_STREQ("SuppRlz", rlz_50);
+ }
+
+ EXPECT_TRUE(rlz_lib::GetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, rlz_50, 50));
+ EXPECT_STREQ("IeTbRlz", rlz_50);
+
+}
+
+TEST_F(RlzLibTest, BrandingWithStatefulEvents) {
+ // Don't run these tests if a supplementary brand is already in place. That
+ // way we can control the branding.
+ if (!rlz_lib::SupplementaryBranding::GetBrand().empty())
+ return;
+
+ const char* kPingResponse =
+ "version: 3.0.914.7250\r\n"
+ "url: http://www.corp.google.com/~av/45/opt/SearchWithGoogleUpdate.exe\r\n"
+ "launch-action: custom-action\r\n"
+ "launch-target: SearchWithGoogleUpdate.exe\r\n"
+ "signature: c08a3f4438e1442c4fe5678ee147cf6c5516e5d62bb64e\r\n"
+ "rlzT4: 1T4_____en__252\r\n" // GoodRLZ.
+ "events: I7S,W1I\r\n" // Clear all events.
+ "stateful-events: W1I\r\n" // W1I as an stateful event.
+ "rlz\r\n"
+ "dcc: dcc_value\r\n"
+ "crc32: 55191759";
+
+ EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
+ {
+ rlz_lib::SupplementaryBranding branding("TEST");
+ EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
+ }
+
+ // Record some product events for the default and supplementary brand.
+ // Check that they get cleared only for the default brand.
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
+
+ {
+ rlz_lib::SupplementaryBranding branding("TEST");
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
+ }
+
+ EXPECT_TRUE(rlz_lib::ParsePingResponse(rlz_lib::TOOLBAR_NOTIFIER,
+ kPingResponse));
+
+ // Check all the events sent earlier are cleared only for default brand.
+ char value[50];
+ EXPECT_FALSE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ value, 50));
+ EXPECT_STREQ("", value);
+
+ {
+ rlz_lib::SupplementaryBranding branding("TEST");
+ EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ value, 50));
+ EXPECT_STREQ("events=I7S,W1I", value);
+ }
+
+ // Record both events (one is stateless and the other is stateful) again.
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
+
+ // Check the stateful event won't be sent again while the stateless one will.
+ EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ value, 50));
+ EXPECT_STREQ("events=I7S", value);
+
+ {
+ rlz_lib::SupplementaryBranding branding("TEST");
+ EXPECT_TRUE(rlz_lib::ParsePingResponse(rlz_lib::TOOLBAR_NOTIFIER,
+ kPingResponse));
+
+ EXPECT_FALSE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ value, 50));
+ EXPECT_STREQ("", value);
+ }
+
+ EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
+ value, 50));
+ EXPECT_STREQ("events=I7S", value);
+}
+
+#if defined(OS_POSIX)
+class ReadonlyRlzDirectoryTest : public RlzLibTestNoMachineState {
+ protected:
+ virtual void SetUp() OVERRIDE;
+};
+
+void ReadonlyRlzDirectoryTest::SetUp() {
+ RlzLibTestNoMachineState::SetUp();
+ // Make the rlz directory non-writeable.
+ int chmod_result = chmod(temp_dir_.path().value().c_str(), 0500);
+ ASSERT_EQ(0, chmod_result);
+}
+
+TEST_F(ReadonlyRlzDirectoryTest, WriteFails) {
+ // The rlz test runner runs every test twice: Once normally, and once with
+ // a SupplementaryBranding on the stack. In the latter case, the rlz lock
+ // has already been acquired before the rlz directory got changed to
+ // read-only, which makes this test pointless. So run it only in the first
+ // pass.
+ if (!rlz_lib::SupplementaryBranding::GetBrand().empty())
+ return;
+
+ EXPECT_FALSE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
+}
+
+// Regression test for http://crbug.com/121255
+TEST_F(ReadonlyRlzDirectoryTest, SupplementaryBrandingDoesNotCrash) {
+ // See the comment at the top of WriteFails.
+ if (!rlz_lib::SupplementaryBranding::GetBrand().empty())
+ return;
+
+ rlz_lib::SupplementaryBranding branding("TEST");
+ EXPECT_FALSE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::INSTALL));
+}
+
+// Regression test for http://crbug.com/141108
+TEST_F(RlzLibTest, ConcurrentStoreAccessWithProcessExitsWhileLockHeld) {
+ // See the comment at the top of WriteFails.
+ if (!rlz_lib::SupplementaryBranding::GetBrand().empty())
+ return;
+
+ std::vector<pid_t> pids;
+ for (int i = 0; i < 10; ++i) {
+ pid_t pid = fork();
+ ASSERT_NE(-1, pid);
+ if (pid == 0) {
+ // Child.
+ {
+ // SupplementaryBranding is a RAII object for the rlz lock.
+ rlz_lib::SupplementaryBranding branding("TEST");
+
+ // Simulate a crash while holding the lock in some of the children.
+ if (i > 0 && i % 3 == 0)
+ _exit(0);
+
+ // Note: Since this is in a forked child, a failing expectation won't
+ // make the test fail. It does however cause lots of "check failed"
+ // error output. The parent process will then check the exit code
+ // below to make the test fail.
+ bool success = rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::INSTALL);
+ EXPECT_TRUE(success);
+ _exit(success ? 0 : 1);
+ }
+ _exit(0);
+ } else {
+ // Parent.
+ pids.push_back(pid);
+ }
+ }
+
+ int status;
+ for (size_t i = 0; i < pids.size(); ++i) {
+ if (HANDLE_EINTR(waitpid(pids[i], &status, 0)) != -1)
+ EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
+ }
+
+ // No child should have the lock at this point, not even the crashed ones.
+ EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::INSTALL));
+}
+
+TEST_F(RlzLibTest, LockAcquistionSucceedsButStoreFileCannotBeCreated) {
+ // See the comment at the top of WriteFails.
+ if (!rlz_lib::SupplementaryBranding::GetBrand().empty())
+ return;
+
+ // Create a directory where the rlz file is supposed to appear. This way,
+ // the lock file can be created successfully, but creation of the rlz file
+ // itself will fail.
+ int mkdir_result = mkdir(rlz_lib::testing::RlzStoreFilenameStr().c_str(),
+ 0500);
+ ASSERT_EQ(0, mkdir_result);
+
+ rlz_lib::SupplementaryBranding branding("TEST");
+ EXPECT_FALSE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
+ rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::INSTALL));
+}
+
+#endif
diff --git a/chromium/rlz/lib/rlz_value_store.h b/chromium/rlz/lib/rlz_value_store.h
new file mode 100644
index 00000000000..4bd1f093b83
--- /dev/null
+++ b/chromium/rlz/lib/rlz_value_store.h
@@ -0,0 +1,120 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RLZ_VALUE_STORE_H_
+#define RLZ_VALUE_STORE_H_
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "rlz/lib/rlz_enums.h"
+
+#if defined(OS_WIN)
+#include "rlz/win/lib/lib_mutex.h"
+#endif
+
+#if defined(OS_MACOSX)
+#include "base/mac/scoped_nsautorelease_pool.h"
+#endif
+
+
+#include <string>
+#include <vector>
+
+namespace base {
+class FilePath;
+}
+
+namespace rlz_lib {
+
+// Abstracts away rlz's key value store. On windows, this usually writes to
+// the registry. On mac, it writes to an NSDefaults object.
+class RlzValueStore {
+ public:
+ virtual ~RlzValueStore() {}
+
+ enum AccessType { kReadAccess, kWriteAccess };
+ virtual bool HasAccess(AccessType type) = 0;
+
+ // Ping times.
+ virtual bool WritePingTime(Product product, int64 time) = 0;
+ virtual bool ReadPingTime(Product product, int64* time) = 0;
+ virtual bool ClearPingTime(Product product) = 0;
+
+ // Access point RLZs.
+ virtual bool WriteAccessPointRlz(AccessPoint access_point,
+ const char* new_rlz) = 0;
+ virtual bool ReadAccessPointRlz(AccessPoint access_point,
+ char* rlz, // At most kMaxRlzLength + 1 bytes
+ size_t rlz_size) = 0;
+ virtual bool ClearAccessPointRlz(AccessPoint access_point) = 0;
+
+ // Product events.
+ // Stores |event_rlz| for product |product| as product event.
+ virtual bool AddProductEvent(Product product, const char* event_rlz) = 0;
+ // Appends all events for |product| to |events|, in arbirtrary order.
+ virtual bool ReadProductEvents(Product product,
+ std::vector<std::string>* events) = 0;
+ // Removes the stored event |event_rlz| for |product| if it exists.
+ virtual bool ClearProductEvent(Product product, const char* event_rlz) = 0;
+ // Removes all stored product events for |product|.
+ virtual bool ClearAllProductEvents(Product product) = 0;
+
+ // Stateful events.
+ // Stores |event_rlz| for product |product| as stateful event.
+ virtual bool AddStatefulEvent(Product product, const char* event_rlz) = 0;
+ // Checks if |event_rlz| has been stored as stateful event for |product|.
+ virtual bool IsStatefulEvent(Product product, const char* event_rlz) = 0;
+ // Removes all stored stateful events for |product|.
+ virtual bool ClearAllStatefulEvents(Product product) = 0;
+
+ // Tells the value store to clean up unimportant internal data structures, for
+ // example empty registry folders, that might remain after clearing other
+ // data. Best-effort.
+ virtual void CollectGarbage() = 0;
+};
+
+// All methods of RlzValueStore must stays consistent even when accessed from
+// multiple threads in multiple processes. To enforce this through the type
+// system, the only way to access the RlzValueStore is through a
+// ScopedRlzValueStoreLock, which is a cross-process lock. It is active while
+// it is in scope. If the class fails to acquire a lock, its GetStore() method
+// returns NULL. If the lock fails to be acquired, it must not be taken
+// recursively. That is, all user code should look like this:
+// ScopedRlzValueStoreLock lock;
+// RlzValueStore* store = lock.GetStore();
+// if (!store)
+// return some_error_code;
+// ...
+class ScopedRlzValueStoreLock {
+ public:
+ ScopedRlzValueStoreLock();
+ ~ScopedRlzValueStoreLock();
+
+ // Returns a RlzValueStore protected by a cross-process lock, or NULL if the
+ // lock can't be obtained. The lifetime of the returned object is limited to
+ // the lifetime of this ScopedRlzValueStoreLock object.
+ RlzValueStore* GetStore();
+
+ private:
+ scoped_ptr<RlzValueStore> store_;
+#if defined(OS_WIN)
+ LibMutex lock_;
+#elif defined(OS_MACOSX)
+ base::mac::ScopedNSAutoreleasePool autorelease_pool_;
+#endif
+};
+
+#if defined(OS_POSIX)
+namespace testing {
+// Prefix |directory| to the path where the RLZ data file lives, for tests.
+void SetRlzStoreDirectory(const base::FilePath& directory);
+
+// Returns the path of the file used as data store.
+std::string RlzStoreFilenameStr();
+} // namespace testing
+#endif // defined(OS_POSIX)
+
+} // namespace rlz_lib
+
+#endif // RLZ_VALUE_STORE_H_
diff --git a/chromium/rlz/lib/string_utils.cc b/chromium/rlz/lib/string_utils.cc
new file mode 100644
index 00000000000..87988022e41
--- /dev/null
+++ b/chromium/rlz/lib/string_utils.cc
@@ -0,0 +1,92 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// String manipulation functions used in the RLZ library.
+
+#include "rlz/lib/string_utils.h"
+
+#include "rlz/lib/assert.h"
+
+namespace rlz_lib {
+
+bool IsAscii(unsigned char letter) {
+ return letter < 0x80;
+}
+
+bool GetHexValue(char letter, int* value) {
+ if (!value) {
+ ASSERT_STRING("GetHexValue: Invalid output paramter");
+ return false;
+ }
+ *value = 0;
+
+ if (letter >= '0' && letter <= '9')
+ *value = letter - '0';
+ else if (letter >= 'a' && letter <= 'f')
+ *value = (letter - 'a') + 0xA;
+ else if (letter >= 'A' && letter <= 'F')
+ *value = (letter - 'A') + 0xA;
+ else
+ return false;
+
+ return true;
+}
+
+int HexStringToInteger(const char* text) {
+ if (!text) {
+ ASSERT_STRING("HexStringToInteger: text is NULL.");
+ return 0;
+ }
+
+ int idx = 0;
+ // Ignore leading whitespace.
+ while (text[idx] == '\t' || text[idx] == ' ')
+ idx++;
+
+ if ((text[idx] == '0') &&
+ (text[idx + 1] == 'X' || text[idx + 1] == 'x'))
+ idx +=2; // String is of the form 0x...
+
+ int number = 0;
+ int digit = 0;
+ for (; text[idx] != '\0'; idx++) {
+ if (!GetHexValue(text[idx], &digit)) {
+ // Ignore trailing whitespaces, but assert on other trailing characters.
+ bool only_whitespaces = true;
+ while (only_whitespaces && text[idx])
+ only_whitespaces = (text[idx++] == ' ');
+ if (!only_whitespaces)
+ ASSERT_STRING("HexStringToInteger: text contains non-hex characters.");
+ return number;
+ }
+ number = (number << 4) | digit;
+ }
+
+ return number;
+}
+
+bool BytesToString(const unsigned char* data,
+ int data_len,
+ std::string* string) {
+ if (!string)
+ return false;
+
+ string->clear();
+ if (data_len < 1 || !data)
+ return false;
+
+ static const char kHex[] = "0123456789ABCDEF";
+
+ // Fix the buffer size to begin with to avoid repeated re-allocation.
+ string->resize(data_len * 2);
+ int index = data_len;
+ while (index--) {
+ string->at(2 * index) = kHex[data[index] >> 4]; // high digit
+ string->at(2 * index + 1) = kHex[data[index] & 0x0F]; // low digit
+ }
+
+ return true;
+}
+
+} // namespace rlz_lib
diff --git a/chromium/rlz/lib/string_utils.h b/chromium/rlz/lib/string_utils.h
new file mode 100644
index 00000000000..0893a6c169b
--- /dev/null
+++ b/chromium/rlz/lib/string_utils.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// String manipulation functions used in the RLZ library.
+
+#ifndef RLZ_LIB_STRING_UTILS_H_
+#define RLZ_LIB_STRING_UTILS_H_
+
+#include <string>
+
+namespace rlz_lib {
+
+bool IsAscii(unsigned char letter);
+
+bool BytesToString(const unsigned char* data,
+ int data_len,
+ std::string* string);
+
+bool GetHexValue(char letter, int* value);
+
+int HexStringToInteger(const char* text);
+
+}; // namespace
+
+#endif // RLZ_LIB_STRING_UTILS_H_
diff --git a/chromium/rlz/lib/string_utils_unittest.cc b/chromium/rlz/lib/string_utils_unittest.cc
new file mode 100644
index 00000000000..d8be24a87dd
--- /dev/null
+++ b/chromium/rlz/lib/string_utils_unittest.cc
@@ -0,0 +1,69 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Unit test for string manipulation functions used in the RLZ library.
+
+#include "rlz/lib/string_utils.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "rlz/lib/assert.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(StringUtilsUnittest, IsAscii) {
+ rlz_lib::SetExpectedAssertion("");
+
+ char bad_letters[] = {'\x80', '\xA0', '\xFF'};
+ for (size_t i = 0; i < arraysize(bad_letters); ++i)
+ EXPECT_FALSE(rlz_lib::IsAscii(bad_letters[i]));
+
+ char good_letters[] = {'A', '~', '\n', 0x7F, 0x00};
+ for (size_t i = 0; i < arraysize(good_letters); ++i)
+ EXPECT_TRUE(rlz_lib::IsAscii(good_letters[i]));
+}
+
+TEST(StringUtilsUnittest, HexStringToInteger) {
+ rlz_lib::SetExpectedAssertion("HexStringToInteger: text is NULL.");
+ EXPECT_EQ(0, rlz_lib::HexStringToInteger(NULL));
+
+ rlz_lib::SetExpectedAssertion("");
+ EXPECT_EQ(0, rlz_lib::HexStringToInteger(""));
+ EXPECT_EQ(0, rlz_lib::HexStringToInteger(" "));
+ EXPECT_EQ(0, rlz_lib::HexStringToInteger(" 0x "));
+ EXPECT_EQ(0, rlz_lib::HexStringToInteger(" 0x0 "));
+ EXPECT_EQ(0x12345, rlz_lib::HexStringToInteger("12345"));
+ EXPECT_EQ(0xa34Ed0, rlz_lib::HexStringToInteger("a34Ed0"));
+ EXPECT_EQ(0xa34Ed0, rlz_lib::HexStringToInteger("0xa34Ed0"));
+ EXPECT_EQ(0xa34Ed0, rlz_lib::HexStringToInteger(" 0xa34Ed0"));
+ EXPECT_EQ(0xa34Ed0, rlz_lib::HexStringToInteger("0xa34Ed0 "));
+ EXPECT_EQ(0xa34Ed0, rlz_lib::HexStringToInteger(" 0xa34Ed0 "));
+ EXPECT_EQ(0xa34Ed0, rlz_lib::HexStringToInteger(" 0x000a34Ed0 "));
+ EXPECT_EQ(0xa34Ed0, rlz_lib::HexStringToInteger(" 000a34Ed0 "));
+
+ rlz_lib::SetExpectedAssertion(
+ "HexStringToInteger: text contains non-hex characters.");
+ EXPECT_EQ(0x12ff, rlz_lib::HexStringToInteger("12ffg"));
+ EXPECT_EQ(0x12f, rlz_lib::HexStringToInteger("12f 121"));
+ EXPECT_EQ(0x12f, rlz_lib::HexStringToInteger("12f 121"));
+ EXPECT_EQ(0, rlz_lib::HexStringToInteger("g12f"));
+ EXPECT_EQ(0, rlz_lib::HexStringToInteger(" 0x0 \n"));
+
+ rlz_lib::SetExpectedAssertion("");
+}
+
+TEST(StringUtilsUnittest, TestBytesToString) {
+ unsigned char data[] = {0x1E, 0x00, 0x21, 0x67, 0xFF};
+ std::string result;
+
+ EXPECT_FALSE(rlz_lib::BytesToString(NULL, 5, &result));
+ EXPECT_FALSE(rlz_lib::BytesToString(data, 5, NULL));
+ EXPECT_FALSE(rlz_lib::BytesToString(NULL, 5, NULL));
+
+ EXPECT_TRUE(rlz_lib::BytesToString(data, 5, &result));
+ EXPECT_EQ(std::string("1E002167FF"), result);
+ EXPECT_TRUE(rlz_lib::BytesToString(data, 4, &result));
+ EXPECT_EQ(std::string("1E002167"), result);
+}
diff --git a/chromium/rlz/mac/lib/machine_id_mac.cc b/chromium/rlz/mac/lib/machine_id_mac.cc
new file mode 100644
index 00000000000..f16175b8a19
--- /dev/null
+++ b/chromium/rlz/mac/lib/machine_id_mac.cc
@@ -0,0 +1,148 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/network/IOEthernetController.h>
+#include <IOKit/network/IOEthernetInterface.h>
+#include <IOKit/network/IONetworkInterface.h>
+
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/mac/scoped_ioobject.h"
+#include "base/strings/string16.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+
+namespace rlz_lib {
+
+namespace {
+
+// See http://developer.apple.com/library/mac/#technotes/tn1103/_index.html
+
+// The caller is responsible for freeing |matching_services|.
+bool FindEthernetInterfaces(io_iterator_t* matching_services) {
+ base::ScopedCFTypeRef<CFMutableDictionaryRef> matching_dict(
+ IOServiceMatching(kIOEthernetInterfaceClass));
+ if (!matching_dict)
+ return false;
+
+ base::ScopedCFTypeRef<CFMutableDictionaryRef> primary_interface(
+ CFDictionaryCreateMutable(kCFAllocatorDefault,
+ 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+ if (!primary_interface)
+ return false;
+
+ CFDictionarySetValue(
+ primary_interface, CFSTR(kIOPrimaryInterface), kCFBooleanTrue);
+ CFDictionarySetValue(
+ matching_dict, CFSTR(kIOPropertyMatchKey), primary_interface);
+
+ kern_return_t kern_result = IOServiceGetMatchingServices(
+ kIOMasterPortDefault, matching_dict.release(), matching_services);
+
+ return kern_result == KERN_SUCCESS;
+}
+
+bool GetMACAddressFromIterator(io_iterator_t primary_interface_iterator,
+ uint8_t* buffer, size_t buffer_size) {
+ if (buffer_size < kIOEthernetAddressSize)
+ return false;
+
+ bool success = false;
+
+ bzero(buffer, buffer_size);
+ base::mac::ScopedIOObject<io_object_t> primary_interface;
+ while (primary_interface.reset(IOIteratorNext(primary_interface_iterator)),
+ primary_interface) {
+ io_object_t primary_interface_parent;
+ kern_return_t kern_result = IORegistryEntryGetParentEntry(
+ primary_interface, kIOServicePlane, &primary_interface_parent);
+ base::mac::ScopedIOObject<io_object_t> primary_interface_parent_deleter(
+ primary_interface_parent);
+ success = kern_result == KERN_SUCCESS;
+
+ if (!success)
+ continue;
+
+ base::ScopedCFTypeRef<CFTypeRef> mac_data(
+ IORegistryEntryCreateCFProperty(primary_interface_parent,
+ CFSTR(kIOMACAddress),
+ kCFAllocatorDefault,
+ 0));
+ CFDataRef mac_data_data = base::mac::CFCast<CFDataRef>(mac_data);
+ if (mac_data_data) {
+ CFDataGetBytes(
+ mac_data_data, CFRangeMake(0, kIOEthernetAddressSize), buffer);
+ }
+ }
+
+ return success;
+}
+
+bool GetMacAddress(unsigned char* buffer, size_t size) {
+ io_iterator_t primary_interface_iterator;
+ if (!FindEthernetInterfaces(&primary_interface_iterator))
+ return false;
+ bool result = GetMACAddressFromIterator(
+ primary_interface_iterator, buffer, size);
+ IOObjectRelease(primary_interface_iterator);
+ return result;
+}
+
+CFStringRef CopySerialNumber() {
+ base::mac::ScopedIOObject<io_service_t> expert_device(
+ IOServiceGetMatchingService(kIOMasterPortDefault,
+ IOServiceMatching("IOPlatformExpertDevice")));
+ if (!expert_device)
+ return NULL;
+
+ base::ScopedCFTypeRef<CFTypeRef> serial_number(
+ IORegistryEntryCreateCFProperty(expert_device,
+ CFSTR(kIOPlatformSerialNumberKey),
+ kCFAllocatorDefault,
+ 0));
+ CFStringRef serial_number_cfstring =
+ base::mac::CFCast<CFStringRef>(serial_number);
+ if (!serial_number_cfstring)
+ return NULL;
+
+ ignore_result(serial_number.release());
+ return serial_number_cfstring;
+}
+
+} // namespace
+
+bool GetRawMachineId(string16* data, int* more_data) {
+ uint8_t mac_address[kIOEthernetAddressSize];
+
+ data->clear();
+ if (GetMacAddress(mac_address, sizeof(mac_address))) {
+ *data += ASCIIToUTF16(base::StringPrintf("mac:%02x%02x%02x%02x%02x%02x",
+ mac_address[0], mac_address[1], mac_address[2],
+ mac_address[3], mac_address[4], mac_address[5]));
+ }
+
+ // A MAC address is enough to uniquely identify a machine, but it's only 6
+ // bytes, 3 of which are manufacturer-determined. To make brute-forcing the
+ // SHA1 of this harder, also append the system's serial number.
+ CFStringRef serial = CopySerialNumber();
+ if (serial) {
+ if (!data->empty())
+ *data += UTF8ToUTF16(" ");
+ *data += UTF8ToUTF16("serial:") + base::SysCFStringRefToUTF16(serial);
+ CFRelease(serial);
+ }
+
+ // On windows, this is set to the volume id. Since it's not scrambled before
+ // being sent, just set it to 1.
+ *more_data = 1;
+ return true;
+}
+
+} // namespace rlz_lib
diff --git a/chromium/rlz/mac/lib/rlz_value_store_mac.h b/chromium/rlz/mac/lib/rlz_value_store_mac.h
new file mode 100644
index 00000000000..117e901bc76
--- /dev/null
+++ b/chromium/rlz/mac/lib/rlz_value_store_mac.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RLZ_MAC_LIB_RLZ_VALUE_STORE_MAC_H_
+#define RLZ_MAC_LIB_RLZ_VALUE_STORE_MAC_H_
+
+#include "base/compiler_specific.h"
+#include "base/mac/scoped_nsobject.h"
+#include "rlz/lib/rlz_value_store.h"
+
+@class NSDictionary;
+@class NSMutableDictionary;
+
+namespace rlz_lib {
+
+// An implementation of RlzValueStore for mac. It stores information in a
+// plist file in the user's Application Support folder.
+class RlzValueStoreMac : public RlzValueStore {
+ public:
+ virtual bool HasAccess(AccessType type) OVERRIDE;
+
+ virtual bool WritePingTime(Product product, int64 time) OVERRIDE;
+ virtual bool ReadPingTime(Product product, int64* time) OVERRIDE;
+ virtual bool ClearPingTime(Product product) OVERRIDE;
+
+ virtual bool WriteAccessPointRlz(AccessPoint access_point,
+ const char* new_rlz) OVERRIDE;
+ virtual bool ReadAccessPointRlz(AccessPoint access_point,
+ char* rlz,
+ size_t rlz_size) OVERRIDE;
+ virtual bool ClearAccessPointRlz(AccessPoint access_point) OVERRIDE;
+
+ virtual bool AddProductEvent(Product product, const char* event_rlz) OVERRIDE;
+ virtual bool ReadProductEvents(Product product,
+ std::vector<std::string>* events) OVERRIDE;
+ virtual bool ClearProductEvent(Product product,
+ const char* event_rlz) OVERRIDE;
+ virtual bool ClearAllProductEvents(Product product) OVERRIDE;
+
+ virtual bool AddStatefulEvent(Product product,
+ const char* event_rlz) OVERRIDE;
+ virtual bool IsStatefulEvent(Product product,
+ const char* event_rlz) OVERRIDE;
+ virtual bool ClearAllStatefulEvents(Product product) OVERRIDE;
+
+ virtual void CollectGarbage() OVERRIDE;
+
+ private:
+ // |dict| is the dictionary that backs all data. plist_path is the name of the
+ // plist file, used solely for implementing HasAccess().
+ RlzValueStoreMac(NSMutableDictionary* dict, NSString* plist_path);
+ virtual ~RlzValueStoreMac();
+ friend class ScopedRlzValueStoreLock;
+
+ // Returns the backing dictionary that should be written to disk.
+ NSDictionary* dictionary();
+
+ // Returns the dictionary to which all data should be written. Usually, this
+ // is just |dictionary()|, but if supplementary branding is used, it's a
+ // subdirectory at key "brand_<supplementary branding code>".
+ // Note that windows stores data at
+ // rlz/name (e.g. "pingtime")/supplementalbranding/productcode
+ // Mac on the other hand does
+ // supplementalbranding/productcode/pingtime.
+ NSMutableDictionary* WorkingDict();
+
+ // Returns the subdirectory of |WorkingDict()| used to store data for
+ // product p.
+ NSMutableDictionary* ProductDict(Product p);
+
+ base::scoped_nsobject<NSMutableDictionary> dict_;
+ base::scoped_nsobject<NSString> plist_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(RlzValueStoreMac);
+};
+
+} // namespace rlz_lib
+
+#endif // RLZ_MAC_LIB_RLZ_VALUE_STORE_MAC_H_
diff --git a/chromium/rlz/mac/lib/rlz_value_store_mac.mm b/chromium/rlz/mac/lib/rlz_value_store_mac.mm
new file mode 100644
index 00000000000..158fd88502b
--- /dev/null
+++ b/chromium/rlz/mac/lib/rlz_value_store_mac.mm
@@ -0,0 +1,373 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "rlz/mac/lib/rlz_value_store_mac.h"
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/strings/sys_string_conversions.h"
+#include "rlz/lib/assert.h"
+#include "rlz/lib/lib_values.h"
+#include "rlz/lib/recursive_cross_process_lock_posix.h"
+#include "rlz/lib/rlz_lib.h"
+
+#import <Foundation/Foundation.h>
+#include <pthread.h>
+
+using base::mac::ObjCCast;
+
+namespace rlz_lib {
+
+// These are written to disk and should not be changed.
+NSString* const kPingTimeKey = @"pingTime";
+NSString* const kAccessPointKey = @"accessPoints";
+NSString* const kProductEventKey = @"productEvents";
+NSString* const kStatefulEventKey = @"statefulEvents";
+
+namespace {
+
+NSString* GetNSProductName(Product product) {
+ return base::SysUTF8ToNSString(GetProductName(product));
+}
+
+NSString* GetNSAccessPointName(AccessPoint p) {
+ return base::SysUTF8ToNSString(GetAccessPointName(p));
+}
+
+// Retrieves a subdictionary in |p| for key |k|, creating it if necessary.
+// If the dictionary contains an object for |k| that is not a mutable
+// dictionary, that object is replaced with an empty mutable dictinary.
+NSMutableDictionary* GetOrCreateDict(
+ NSMutableDictionary* p, NSString* k) {
+ NSMutableDictionary* d = ObjCCast<NSMutableDictionary>([p objectForKey:k]);
+ if (!d) {
+ d = [NSMutableDictionary dictionaryWithCapacity:0];
+ [p setObject:d forKey:k];
+ }
+ return d;
+}
+
+} // namespace
+
+RlzValueStoreMac::RlzValueStoreMac(NSMutableDictionary* dict,
+ NSString* plist_path)
+ : dict_([dict retain]), plist_path_([plist_path retain]) {
+}
+
+RlzValueStoreMac::~RlzValueStoreMac() {
+}
+
+bool RlzValueStoreMac::HasAccess(AccessType type) {
+ NSFileManager* manager = [NSFileManager defaultManager];
+ switch (type) {
+ case kReadAccess: return [manager isReadableFileAtPath:plist_path_];
+ case kWriteAccess: return [manager isWritableFileAtPath:plist_path_];
+ }
+}
+
+bool RlzValueStoreMac::WritePingTime(Product product, int64 time) {
+ NSNumber* n = [NSNumber numberWithLongLong:time];
+ [ProductDict(product) setObject:n forKey:kPingTimeKey];
+ return true;
+}
+
+bool RlzValueStoreMac::ReadPingTime(Product product, int64* time) {
+ if (NSNumber* n =
+ ObjCCast<NSNumber>([ProductDict(product) objectForKey:kPingTimeKey])) {
+ *time = [n longLongValue];
+ return true;
+ }
+ return false;
+}
+
+bool RlzValueStoreMac::ClearPingTime(Product product) {
+ [ProductDict(product) removeObjectForKey:kPingTimeKey];
+ return true;
+}
+
+
+bool RlzValueStoreMac::WriteAccessPointRlz(AccessPoint access_point,
+ const char* new_rlz) {
+ NSMutableDictionary* d = GetOrCreateDict(WorkingDict(), kAccessPointKey);
+ [d setObject:base::SysUTF8ToNSString(new_rlz)
+ forKey:GetNSAccessPointName(access_point)];
+ return true;
+}
+
+bool RlzValueStoreMac::ReadAccessPointRlz(AccessPoint access_point,
+ char* rlz,
+ size_t rlz_size) {
+ // Reading a non-existent access point counts as success.
+ if (NSDictionary* d = ObjCCast<NSDictionary>(
+ [WorkingDict() objectForKey:kAccessPointKey])) {
+ NSString* val = ObjCCast<NSString>(
+ [d objectForKey:GetNSAccessPointName(access_point)]);
+ if (!val) {
+ if (rlz_size > 0)
+ rlz[0] = '\0';
+ return true;
+ }
+
+ std::string s = base::SysNSStringToUTF8(val);
+ if (s.size() >= rlz_size) {
+ rlz[0] = 0;
+ ASSERT_STRING("GetAccessPointRlz: Insufficient buffer size");
+ return false;
+ }
+ strncpy(rlz, s.c_str(), rlz_size);
+ return true;
+ }
+ if (rlz_size > 0)
+ rlz[0] = '\0';
+ return true;
+}
+
+bool RlzValueStoreMac::ClearAccessPointRlz(AccessPoint access_point) {
+ if (NSMutableDictionary* d = ObjCCast<NSMutableDictionary>(
+ [WorkingDict() objectForKey:kAccessPointKey])) {
+ [d removeObjectForKey:GetNSAccessPointName(access_point)];
+ }
+ return true;
+}
+
+
+bool RlzValueStoreMac::AddProductEvent(Product product,
+ const char* event_rlz) {
+ [GetOrCreateDict(ProductDict(product), kProductEventKey)
+ setObject:[NSNumber numberWithBool:YES]
+ forKey:base::SysUTF8ToNSString(event_rlz)];
+ return true;
+}
+
+bool RlzValueStoreMac::ReadProductEvents(Product product,
+ std::vector<std::string>* events) {
+ if (NSDictionary* d = ObjCCast<NSDictionary>(
+ [ProductDict(product) objectForKey:kProductEventKey])) {
+ for (NSString* s in d)
+ events->push_back(base::SysNSStringToUTF8(s));
+ return true;
+ }
+ return true;
+}
+
+bool RlzValueStoreMac::ClearProductEvent(Product product,
+ const char* event_rlz) {
+ if (NSMutableDictionary* d = ObjCCast<NSMutableDictionary>(
+ [ProductDict(product) objectForKey:kProductEventKey])) {
+ [d removeObjectForKey:base::SysUTF8ToNSString(event_rlz)];
+ return true;
+ }
+ return false;
+}
+
+bool RlzValueStoreMac::ClearAllProductEvents(Product product) {
+ [ProductDict(product) removeObjectForKey:kProductEventKey];
+ return true;
+}
+
+
+bool RlzValueStoreMac::AddStatefulEvent(Product product,
+ const char* event_rlz) {
+ [GetOrCreateDict(ProductDict(product), kStatefulEventKey)
+ setObject:[NSNumber numberWithBool:YES]
+ forKey:base::SysUTF8ToNSString(event_rlz)];
+ return true;
+}
+
+bool RlzValueStoreMac::IsStatefulEvent(Product product,
+ const char* event_rlz) {
+ if (NSDictionary* d = ObjCCast<NSDictionary>(
+ [ProductDict(product) objectForKey:kStatefulEventKey])) {
+ return [d objectForKey:base::SysUTF8ToNSString(event_rlz)] != nil;
+ }
+ return false;
+}
+
+bool RlzValueStoreMac::ClearAllStatefulEvents(Product product) {
+ [ProductDict(product) removeObjectForKey:kStatefulEventKey];
+ return true;
+}
+
+
+void RlzValueStoreMac::CollectGarbage() {
+ NOTIMPLEMENTED();
+}
+
+NSDictionary* RlzValueStoreMac::dictionary() {
+ return dict_.get();
+}
+
+NSMutableDictionary* RlzValueStoreMac::WorkingDict() {
+ std::string brand(SupplementaryBranding::GetBrand());
+ if (brand.empty())
+ return dict_;
+
+ NSString* brand_ns =
+ [@"brand_" stringByAppendingString:base::SysUTF8ToNSString(brand)];
+
+ return GetOrCreateDict(dict_.get(), brand_ns);
+}
+
+NSMutableDictionary* RlzValueStoreMac::ProductDict(Product p) {
+ return GetOrCreateDict(WorkingDict(), GetNSProductName(p));
+}
+
+
+namespace {
+
+RecursiveCrossProcessLock g_recursive_lock =
+ RECURSIVE_CROSS_PROCESS_LOCK_INITIALIZER;
+
+// This is set during test execution, to write RLZ files into a temporary
+// directory instead of the user's Application Support folder.
+NSString* g_test_folder;
+
+// RlzValueStoreMac keeps its data in memory and only writes it to disk when
+// ScopedRlzValueStoreLock goes out of scope. Hence, if several
+// ScopedRlzValueStoreLocks are nested, they all need to use the same store
+// object.
+
+// This counts the nesting depth.
+int g_lock_depth = 0;
+
+// This is the store object that might be shared. Only set if g_lock_depth > 0.
+RlzValueStoreMac* g_store_object = NULL;
+
+
+NSString* CreateRlzDirectory() {
+ NSFileManager* manager = [NSFileManager defaultManager];
+ NSArray* paths = NSSearchPathForDirectoriesInDomains(
+ NSApplicationSupportDirectory, NSUserDomainMask, /*expandTilde=*/YES);
+ NSString* folder = nil;
+ if ([paths count] > 0)
+ folder = ObjCCast<NSString>([paths objectAtIndex:0]);
+ if (!folder)
+ folder = [@"~/Library/Application Support" stringByStandardizingPath];
+ folder = [folder stringByAppendingPathComponent:@"Google/RLZ"];
+
+ if (g_test_folder)
+ folder = [g_test_folder stringByAppendingPathComponent:folder];
+
+ [manager createDirectoryAtPath:folder
+ withIntermediateDirectories:YES
+ attributes:nil
+ error:nil];
+ return folder;
+}
+
+// Returns the path of the rlz plist store, also creates the parent directory
+// path if it doesn't exist.
+NSString* RlzPlistFilename() {
+ NSString* const kRlzFile = @"RlzStore.plist";
+ return [CreateRlzDirectory() stringByAppendingPathComponent:kRlzFile];
+}
+
+// Returns the path of the rlz lock file, also creates the parent directory
+// path if it doesn't exist.
+NSString* RlzLockFilename() {
+ NSString* const kRlzLockfile = @"flockfile";
+ return [CreateRlzDirectory() stringByAppendingPathComponent:kRlzLockfile];
+}
+
+} // namespace
+
+ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() {
+ bool got_distributed_lock = g_recursive_lock.TryGetCrossProcessLock(
+ base::FilePath([RlzLockFilename() fileSystemRepresentation]));
+ // At this point, we hold the in-process lock, no matter the value of
+ // |got_distributed_lock|.
+
+ ++g_lock_depth;
+
+ if (!got_distributed_lock) {
+ // Give up. |store_| isn't set, which signals to callers that acquiring
+ // the lock failed. |g_recursive_lock| will be released by the
+ // destructor.
+ CHECK(!g_store_object);
+ return;
+ }
+
+ if (g_lock_depth > 1) {
+ // Reuse the already existing store object. Note that it can be NULL when
+ // lock acquisition succeeded but the rlz data file couldn't be read.
+ store_.reset(g_store_object);
+ return;
+ }
+
+ CHECK(!g_store_object);
+
+ NSString* plist = RlzPlistFilename();
+
+ // Create an empty file if none exists yet.
+ NSFileManager* manager = [NSFileManager defaultManager];
+ if (![manager fileExistsAtPath:plist isDirectory:NULL])
+ [[NSDictionary dictionary] writeToFile:plist atomically:YES];
+
+ NSMutableDictionary* dict =
+ [NSMutableDictionary dictionaryWithContentsOfFile:plist];
+ VERIFY(dict);
+
+ if (dict) {
+ store_.reset(new RlzValueStoreMac(dict, plist));
+ g_store_object = (RlzValueStoreMac*)store_.get();
+ }
+}
+
+ScopedRlzValueStoreLock::~ScopedRlzValueStoreLock() {
+ --g_lock_depth;
+ CHECK(g_lock_depth >= 0);
+
+ if (g_lock_depth > 0) {
+ // Other locks are still using store_, don't free it yet.
+ ignore_result(store_.release());
+ return;
+ }
+
+ if (store_.get()) {
+ g_store_object = NULL;
+
+ NSDictionary* dict =
+ static_cast<RlzValueStoreMac*>(store_.get())->dictionary();
+ VERIFY([dict writeToFile:RlzPlistFilename() atomically:YES]);
+ }
+
+ // Check that "store_ set" => "file_lock acquired". The converse isn't true,
+ // for example if the rlz data file can't be read.
+ if (store_.get())
+ CHECK(g_recursive_lock.file_lock_ != -1);
+ if (g_recursive_lock.file_lock_ == -1)
+ CHECK(!store_.get());
+
+ g_recursive_lock.ReleaseLock();
+}
+
+RlzValueStore* ScopedRlzValueStoreLock::GetStore() {
+ return store_.get();
+}
+
+namespace testing {
+
+void SetRlzStoreDirectory(const base::FilePath& directory) {
+ base::mac::ScopedNSAutoreleasePool pool;
+
+ [g_test_folder release];
+ if (directory.empty()) {
+ g_test_folder = nil;
+ } else {
+ // Not Unsafe on OS X.
+ g_test_folder =
+ [[NSString alloc] initWithUTF8String:directory.AsUTF8Unsafe().c_str()];
+ }
+}
+
+std::string RlzStoreFilenameStr() {
+ @autoreleasepool {
+ return std::string([RlzPlistFilename() fileSystemRepresentation]);
+ }
+}
+
+} // namespace testing
+
+} // namespace rlz_lib
diff --git a/chromium/rlz/rlz.gyp b/chromium/rlz/rlz.gyp
new file mode 100644
index 00000000000..c38c4ff04b6
--- /dev/null
+++ b/chromium/rlz/rlz.gyp
@@ -0,0 +1,166 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ 'variables': {
+ # Force rlz to use chrome's networking stack.
+ 'force_rlz_use_chrome_net%': 1,
+ },
+ 'conditions': [
+ ['force_rlz_use_chrome_net or OS!="win"', {
+ 'rlz_use_chrome_net%': 1,
+ }, {
+ 'rlz_use_chrome_net%': 0,
+ }],
+ ],
+ },
+ 'target_defaults': {
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ 'targets': [
+ {
+ 'target_name': 'rlz_lib',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ ],
+ 'sources': [
+ 'chromeos/lib/machine_id_chromeos.cc',
+ 'chromeos/lib/rlz_value_store_chromeos.cc',
+ 'chromeos/lib/rlz_value_store_chromeos.h',
+ 'lib/assert.cc',
+ 'lib/assert.h',
+ 'lib/crc32.h',
+ 'lib/crc32_wrapper.cc',
+ 'lib/crc8.h',
+ 'lib/crc8.cc',
+ 'lib/financial_ping.cc',
+ 'lib/financial_ping.h',
+ 'lib/lib_values.cc',
+ 'lib/machine_id.cc',
+ 'lib/machine_id.h',
+ 'lib/recursive_cross_process_lock_posix.cc',
+ 'lib/recursive_cross_process_lock_posix.h',
+ 'lib/rlz_enums.h',
+ 'lib/rlz_lib.cc',
+ 'lib/rlz_lib.h',
+ 'lib/rlz_lib_clear.cc',
+ 'lib/lib_values.h',
+ 'lib/rlz_value_store.h',
+ 'lib/string_utils.cc',
+ 'lib/string_utils.h',
+ 'mac/lib/machine_id_mac.cc',
+ 'mac/lib/rlz_value_store_mac.mm',
+ 'mac/lib/rlz_value_store_mac.h',
+ 'win/lib/lib_mutex.cc',
+ 'win/lib/lib_mutex.h',
+ 'win/lib/machine_deal.cc',
+ 'win/lib/machine_deal.h',
+ 'win/lib/machine_id_win.cc',
+ 'win/lib/process_info.cc',
+ 'win/lib/process_info.h',
+ 'win/lib/registry_util.cc',
+ 'win/lib/registry_util.h',
+ 'win/lib/rlz_lib.h',
+ 'win/lib/rlz_lib_win.cc',
+ 'win/lib/rlz_value_store_registry.cc',
+ 'win/lib/rlz_value_store_registry.h',
+ ],
+ 'conditions': [
+ ['rlz_use_chrome_net==1', {
+ 'defines': [
+ 'RLZ_NETWORK_IMPLEMENTATION_CHROME_NET',
+ ],
+ 'direct_dependent_settings': {
+ 'defines': [
+ 'RLZ_NETWORK_IMPLEMENTATION_CHROME_NET',
+ ],
+ },
+ 'dependencies': [
+ '../net/net.gyp:net',
+ '../url/url.gyp:url_lib',
+ ],
+ }, {
+ 'defines': [
+ 'RLZ_NETWORK_IMPLEMENTATION_WIN_INET',
+ ],
+ 'direct_dependent_settings': {
+ 'defines': [
+ 'RLZ_NETWORK_IMPLEMENTATION_WIN_INET',
+ ],
+ },
+ }],
+ ],
+ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ 'msvs_disabled_warnings': [ 4267, ],
+ },
+ {
+ 'target_name': 'rlz_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ ':rlz_lib',
+ '../base/base.gyp:base',
+ '../base/base.gyp:base_prefs',
+ '../testing/gmock.gyp:gmock',
+ '../testing/gtest.gyp:gtest',
+ '../third_party/zlib/zlib.gyp:zlib',
+ ],
+ 'sources': [
+ 'lib/crc32_unittest.cc',
+ 'lib/crc8_unittest.cc',
+ 'lib/financial_ping_test.cc',
+ 'lib/lib_values_unittest.cc',
+ 'lib/machine_id_unittest.cc',
+ 'lib/rlz_lib_test.cc',
+ 'lib/string_utils_unittest.cc',
+ 'test/rlz_test_helpers.cc',
+ 'test/rlz_test_helpers.h',
+ 'test/rlz_unittest_main.cc',
+ 'win/lib/machine_deal_test.cc',
+ ],
+ 'conditions': [
+ ['rlz_use_chrome_net==1', {
+ 'dependencies': [
+ '../net/net.gyp:net_test_support',
+ ],
+ }],
+ ],
+ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ 'msvs_disabled_warnings': [ 4267, ],
+ },
+ {
+ 'target_name': 'rlz_id',
+ 'type': 'executable',
+ 'dependencies': [
+ ':rlz_lib',
+ ],
+ 'sources': [
+ 'examples/rlz_id.cc',
+ ],
+ },
+ ],
+ 'conditions': [
+ ['OS=="win"', {
+ 'targets': [
+ {
+ 'target_name': 'rlz',
+ 'type': 'shared_library',
+ 'sources': [
+ 'win/dll/dll_main.cc',
+ 'win/dll/exports.cc',
+ ],
+ 'dependencies': [
+ ':rlz_lib',
+ '../third_party/zlib/zlib.gyp:zlib',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/chromium/rlz/win/dll/dll_main.cc b/chromium/rlz/win/dll/dll_main.cc
new file mode 100644
index 00000000000..943f517c117
--- /dev/null
+++ b/chromium/rlz/win/dll/dll_main.cc
@@ -0,0 +1,15 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Main function for the RLZ DLL.
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501
+#endif
+#include <windows.h>
+
+BOOL APIENTRY DllMain(HANDLE module, DWORD reason, LPVOID reserved) {
+ return TRUE;
+}
+
diff --git a/chromium/rlz/win/dll/exports.cc b/chromium/rlz/win/dll/exports.cc
new file mode 100644
index 00000000000..efe5cd984e7
--- /dev/null
+++ b/chromium/rlz/win/dll/exports.cc
@@ -0,0 +1,108 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Functions exported by the RLZ DLL.
+
+#include "rlz/win/lib/rlz_lib.h"
+
+#define RLZ_DLL_EXPORT extern "C" __declspec(dllexport)
+
+RLZ_DLL_EXPORT bool RecordProductEvent(rlz_lib::Product product,
+ rlz_lib::AccessPoint point,
+ rlz_lib::Event event_id) {
+ return rlz_lib::RecordProductEvent(product, point, event_id);
+}
+
+RLZ_DLL_EXPORT bool GetProductEventsAsCgi(rlz_lib::Product product,
+ char* unescaped_cgi,
+ size_t unescaped_cgi_size) {
+ return rlz_lib::GetProductEventsAsCgi(product, unescaped_cgi,
+ unescaped_cgi_size);
+}
+RLZ_DLL_EXPORT bool ClearAllProductEvents(rlz_lib::Product product) {
+ return rlz_lib::ClearAllProductEvents(product);
+}
+
+RLZ_DLL_EXPORT bool ClearProductEvent(rlz_lib::Product product,
+ rlz_lib::AccessPoint point,
+ rlz_lib::Event event_id) {
+ return rlz_lib::ClearProductEvent(product, point, event_id);
+}
+
+RLZ_DLL_EXPORT bool GetAccessPointRlz(rlz_lib::AccessPoint point,
+ char* rlz,
+ size_t rlz_size) {
+ return rlz_lib::GetAccessPointRlz(point, rlz, rlz_size);
+}
+
+RLZ_DLL_EXPORT bool SetAccessPointRlz(rlz_lib::AccessPoint point,
+ const char* new_rlz) {
+ return rlz_lib::SetAccessPointRlz(point, new_rlz);
+}
+
+RLZ_DLL_EXPORT bool CreateMachineState() {
+ return rlz_lib::CreateMachineState();
+}
+
+RLZ_DLL_EXPORT bool SetMachineDealCode(const char* dcc) {
+ return rlz_lib::SetMachineDealCode(dcc);
+}
+
+RLZ_DLL_EXPORT bool GetMachineDealCodeAsCgi(char* cgi, size_t cgi_size) {
+ return rlz_lib::GetMachineDealCodeAsCgi(cgi, cgi_size);
+}
+
+RLZ_DLL_EXPORT bool GetMachineDealCode2(char* dcc, size_t dcc_size) {
+ return rlz_lib::GetMachineDealCode(dcc, dcc_size);
+}
+
+RLZ_DLL_EXPORT bool GetPingParams(rlz_lib::Product product,
+ const rlz_lib::AccessPoint* access_points,
+ char* unescaped_cgi,
+ size_t unescaped_cgi_size) {
+ return rlz_lib::GetPingParams(product, access_points, unescaped_cgi,
+ unescaped_cgi_size);
+}
+
+RLZ_DLL_EXPORT bool ParsePingResponse(rlz_lib::Product product,
+ const char* response) {
+ return rlz_lib::ParsePingResponse(product, response);
+}
+
+RLZ_DLL_EXPORT bool IsPingResponseValid(const char* response,
+ int* checksum_idx) {
+ return rlz_lib::IsPingResponseValid(response, checksum_idx);
+}
+
+RLZ_DLL_EXPORT bool SetMachineDealCodeFromPingResponse(const char* response) {
+ return rlz_lib::SetMachineDealCodeFromPingResponse(response);
+}
+
+RLZ_DLL_EXPORT bool SendFinancialPing(rlz_lib::Product product,
+ const rlz_lib::AccessPoint* access_points,
+ const char* product_signature,
+ const char* product_brand,
+ const char* product_id,
+ const char* product_lang,
+ bool exclude_machine_id) {
+ return rlz_lib::SendFinancialPing(product, access_points, product_signature,
+ product_brand, product_id, product_lang, exclude_machine_id);
+}
+
+RLZ_DLL_EXPORT bool SendFinancialPingNoDelay(
+ rlz_lib::Product product,
+ const rlz_lib::AccessPoint* access_points,
+ const char* product_signature,
+ const char* product_brand,
+ const char* product_id,
+ const char* product_lang,
+ bool exclude_machine_id) {
+ return rlz_lib::SendFinancialPing(product, access_points, product_signature,
+ product_brand, product_id, product_lang, exclude_machine_id, true);
+}
+
+RLZ_DLL_EXPORT void ClearProductState(
+ rlz_lib::Product product, const rlz_lib::AccessPoint* access_points) {
+ return rlz_lib::ClearProductState(product, access_points);
+}
diff --git a/chromium/rlz/win/lib/lib_mutex.cc b/chromium/rlz/win/lib/lib_mutex.cc
new file mode 100644
index 00000000000..73dfaded0f7
--- /dev/null
+++ b/chromium/rlz/win/lib/lib_mutex.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Mutex to guarantee serialization of RLZ key accesses.
+
+#include "rlz/win/lib/lib_mutex.h"
+
+#include <windows.h>
+#include <Sddl.h> // For SDDL_REVISION_1, ConvertStringSecurityDescript..
+#include <Aclapi.h> // For SetSecurityInfo
+
+#include "base/logging.h"
+#include "base/win/windows_version.h"
+
+namespace {
+
+const wchar_t kMutexName[] = L"{A946A6A9-917E-4949-B9BC-6BADA8C7FD63}";
+
+} // namespace anonymous
+
+namespace rlz_lib {
+
+// Needed to allow synchronization across integrity levels.
+static bool SetObjectToLowIntegrity(HANDLE object,
+ SE_OBJECT_TYPE type = SE_KERNEL_OBJECT) {
+ if (base::win::GetVersion() < base::win::VERSION_VISTA)
+ return true; // Not needed on XP.
+
+ // The LABEL_SECURITY_INFORMATION SDDL SACL to be set for low integrity.
+ static const wchar_t kLowIntegritySddlSacl[] = L"S:(ML;;NW;;;LW)";
+
+ bool result = false;
+ DWORD error = ERROR_SUCCESS;
+ PSECURITY_DESCRIPTOR security_descriptor = NULL;
+ PACL sacl = NULL;
+ BOOL sacl_present = FALSE;
+ BOOL sacl_defaulted = FALSE;
+
+ if (ConvertStringSecurityDescriptorToSecurityDescriptorW(
+ kLowIntegritySddlSacl, SDDL_REVISION_1, &security_descriptor, NULL)) {
+ if (GetSecurityDescriptorSacl(security_descriptor, &sacl_present,
+ &sacl, &sacl_defaulted)) {
+ error = SetSecurityInfo(object, type, LABEL_SECURITY_INFORMATION,
+ NULL, NULL, NULL, sacl);
+ result = (ERROR_SUCCESS == error);
+ }
+ LocalFree(security_descriptor);
+ }
+
+ return result;
+}
+
+LibMutex::LibMutex() : acquired_(false), mutex_(NULL) {
+ mutex_ = CreateMutex(NULL, false, kMutexName);
+ bool result = SetObjectToLowIntegrity(mutex_);
+ if (result) {
+ acquired_ = (WAIT_OBJECT_0 == WaitForSingleObject(mutex_, 5000L));
+ }
+}
+
+LibMutex::~LibMutex() {
+ if (acquired_) ReleaseMutex(mutex_);
+ CloseHandle(mutex_);
+}
+
+} // namespace rlz_lib
diff --git a/chromium/rlz/win/lib/lib_mutex.h b/chromium/rlz/win/lib/lib_mutex.h
new file mode 100644
index 00000000000..6992bea2ed9
--- /dev/null
+++ b/chromium/rlz/win/lib/lib_mutex.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Mutex to guarantee serialization of RLZ key accesses.
+
+#ifndef RLZ_WIN_LIB_LIB_MUTEX_H_
+#define RLZ_WIN_LIB_LIB_MUTEX_H_
+
+#include <windows.h>
+
+namespace rlz_lib {
+
+class LibMutex {
+ public:
+ LibMutex();
+ ~LibMutex();
+
+ bool failed(void) { return !acquired_; }
+
+ private:
+ bool acquired_;
+ HANDLE mutex_;
+};
+
+} // namespace rlz_lib
+
+#endif // RLZ_WIN_LIB_LIB_MUTEX_H_
diff --git a/chromium/rlz/win/lib/machine_deal.cc b/chromium/rlz/win/lib/machine_deal.cc
new file mode 100644
index 00000000000..a12b75c6f15
--- /dev/null
+++ b/chromium/rlz/win/lib/machine_deal.cc
@@ -0,0 +1,300 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Library functions related to the OEM Deal Confirmation Code.
+
+#include "rlz/win/lib/machine_deal.h"
+
+#include <windows.h>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/win/registry.h"
+#include "rlz/lib/assert.h"
+#include "rlz/lib/lib_values.h"
+#include "rlz/win/lib/lib_mutex.h"
+#include "rlz/win/lib/registry_util.h"
+#include "rlz/win/lib/rlz_value_store_registry.h"
+
+const wchar_t kDccValueName[] = L"DCC";
+
+namespace {
+
+// Current DCC can only uses [a-zA-Z0-9_-!@$*();.<>,:]
+// We will be more liberal and allow some additional chars, but not url meta
+// chars.
+bool IsGoodDccChar(char ch) {
+ if (IsAsciiAlpha(ch) || IsAsciiDigit(ch))
+ return true;
+
+ switch (ch) {
+ case '_':
+ case '-':
+ case '!':
+ case '@':
+ case '$':
+ case '*':
+ case '(':
+ case ')':
+ case ';':
+ case '.':
+ case '<':
+ case '>':
+ case ',':
+ case ':':
+ return true;
+ }
+
+ return false;
+}
+
+// This function will remove bad rlz chars and also limit the max rlz to some
+// reasonable size. It also assumes that normalized_dcc is at least
+// kMaxDccLength+1 long.
+void NormalizeDcc(const char* raw_dcc, char* normalized_dcc) {
+ int index = 0;
+ for (; raw_dcc[index] != 0 && index < rlz_lib::kMaxDccLength; ++index) {
+ char current = raw_dcc[index];
+ if (IsGoodDccChar(current)) {
+ normalized_dcc[index] = current;
+ } else {
+ normalized_dcc[index] = '.';
+ }
+ }
+
+ normalized_dcc[index] = 0;
+}
+
+bool GetResponseLine(const char* response_text, int response_length,
+ int* search_index, std::string* response_line) {
+ if (!response_line || !search_index || *search_index > response_length)
+ return false;
+
+ response_line->clear();
+
+ if (*search_index < 0)
+ return false;
+
+ int line_begin = *search_index;
+ const char* line_end = strchr(response_text + line_begin, '\n');
+
+ if (line_end == NULL || line_end - response_text > response_length) {
+ line_end = response_text + response_length;
+ *search_index = -1;
+ } else {
+ *search_index = line_end - response_text + 1;
+ }
+
+ response_line->assign(response_text + line_begin,
+ line_end - response_text - line_begin);
+ return true;
+}
+
+bool GetResponseValue(const std::string& response_line,
+ const std::string& response_key,
+ std::string* value) {
+ if (!value)
+ return false;
+
+ value->clear();
+
+ if (!StartsWithASCII(response_line, response_key, true))
+ return false;
+
+ std::vector<std::string> tokens;
+ base::SplitString(response_line, ':', &tokens);
+ if (tokens.size() != 2)
+ return false;
+
+ // The first token is the key, the second is the value. The value is already
+ // trimmed for whitespace.
+ *value = tokens[1];
+ return true;
+}
+
+} // namespace anonymous
+
+namespace rlz_lib {
+
+bool MachineDealCode::Set(const char* dcc) {
+ LibMutex lock;
+ if (lock.failed())
+ return false;
+
+ // TODO: if (!ProcessInfo::CanWriteMachineKey()) return false;
+
+ // Validate the new dcc value.
+ size_t length = strlen(dcc);
+ if (length > kMaxDccLength) {
+ ASSERT_STRING("MachineDealCode::Set: DCC length is exceeds max allowed.");
+ return false;
+ }
+
+ base::win::RegKey hklm_key(HKEY_LOCAL_MACHINE,
+ RlzValueStoreRegistry::GetWideLibKeyName().c_str(),
+ KEY_READ | KEY_WRITE | KEY_WOW64_32KEY);
+ if (!hklm_key.Valid()) {
+ ASSERT_STRING("MachineDealCode::Set: Unable to create / open machine key."
+ " Did you call rlz_lib::CreateMachineState()?");
+ return false;
+ }
+
+ char normalized_dcc[kMaxDccLength + 1];
+ NormalizeDcc(dcc, normalized_dcc);
+ VERIFY(length == strlen(normalized_dcc));
+
+ // Write the DCC to HKLM. Note that we need to include the null character
+ // when writing the string.
+ if (!RegKeyWriteValue(hklm_key, kDccValueName, normalized_dcc)) {
+ ASSERT_STRING("MachineDealCode::Set: Could not write the DCC value");
+ return false;
+ }
+
+ return true;
+}
+
+bool MachineDealCode::GetNewCodeFromPingResponse(const char* response,
+ bool* has_new_dcc, char* new_dcc, int new_dcc_size) {
+ if (!has_new_dcc || !new_dcc || !new_dcc_size)
+ return false;
+
+ *has_new_dcc = false;
+ new_dcc[0] = 0;
+
+ int response_length = -1;
+ if (!IsPingResponseValid(response, &response_length))
+ return false;
+
+ // Get the current DCC value to compare to later)
+ char stored_dcc[kMaxDccLength + 1];
+ if (!Get(stored_dcc, arraysize(stored_dcc)))
+ stored_dcc[0] = 0;
+
+ int search_index = 0;
+ std::string response_line;
+ std::string new_dcc_value;
+ bool old_dcc_confirmed = false;
+ const std::string dcc_cgi(kDccCgiVariable);
+ const std::string dcc_cgi_response(kSetDccResponseVariable);
+ while (GetResponseLine(response, response_length, &search_index,
+ &response_line)) {
+ std::string value;
+
+ if (!old_dcc_confirmed &&
+ GetResponseValue(response_line, dcc_cgi, &value)) {
+ // This is the old DCC confirmation - should match value in registry.
+ if (value != stored_dcc)
+ return false; // Corrupted DCC - ignore this response.
+ else
+ old_dcc_confirmed = true;
+ continue;
+ }
+
+ if (!(*has_new_dcc) &&
+ GetResponseValue(response_line, dcc_cgi_response, &value)) {
+ // This is the new DCC.
+ if (value.size() > kMaxDccLength) continue; // Too long
+ *has_new_dcc = true;
+ new_dcc_value = value;
+ }
+ }
+
+ old_dcc_confirmed |= (NULL == stored_dcc[0]);
+
+ base::strlcpy(new_dcc, new_dcc_value.c_str(), new_dcc_size);
+ return old_dcc_confirmed;
+}
+
+bool MachineDealCode::SetFromPingResponse(const char* response) {
+ bool has_new_dcc = false;
+ char new_dcc[kMaxDccLength + 1];
+
+ bool response_valid = GetNewCodeFromPingResponse(
+ response, &has_new_dcc, new_dcc, arraysize(new_dcc));
+
+ if (response_valid && has_new_dcc)
+ return Set(new_dcc);
+
+ return response_valid;
+}
+
+bool MachineDealCode::GetAsCgi(char* cgi, int cgi_size) {
+ if (!cgi || cgi_size <= 0) {
+ ASSERT_STRING("MachineDealCode::GetAsCgi: Invalid buffer");
+ return false;
+ }
+
+ cgi[0] = 0;
+
+ std::string cgi_arg;
+ base::StringAppendF(&cgi_arg, "%s=", kDccCgiVariable);
+ int cgi_arg_length = cgi_arg.size();
+
+ if (cgi_arg_length >= cgi_size) {
+ ASSERT_STRING("MachineDealCode::GetAsCgi: Insufficient buffer size");
+ return false;
+ }
+
+ base::strlcpy(cgi, cgi_arg.c_str(), cgi_size);
+
+ if (!Get(cgi + cgi_arg_length, cgi_size - cgi_arg_length)) {
+ cgi[0] = 0;
+ return false;
+ }
+ return true;
+}
+
+bool MachineDealCode::Get(char* dcc, int dcc_size) {
+ LibMutex lock;
+ if (lock.failed())
+ return false;
+
+ if (!dcc || dcc_size <= 0) {
+ ASSERT_STRING("MachineDealCode::Get: Invalid buffer");
+ return false;
+ }
+
+ dcc[0] = 0;
+
+ base::win::RegKey dcc_key(HKEY_LOCAL_MACHINE,
+ RlzValueStoreRegistry::GetWideLibKeyName().c_str(),
+ KEY_READ | KEY_WOW64_32KEY);
+ if (!dcc_key.Valid())
+ return false; // no DCC key.
+
+ size_t size = dcc_size;
+ if (!RegKeyReadValue(dcc_key, kDccValueName, dcc, &size)) {
+ ASSERT_STRING("MachineDealCode::Get: Insufficient buffer size");
+ dcc[0] = 0;
+ return false;
+ }
+
+ return true;
+}
+
+bool MachineDealCode::Clear() {
+ base::win::RegKey dcc_key(HKEY_LOCAL_MACHINE,
+ RlzValueStoreRegistry::GetWideLibKeyName().c_str(),
+ KEY_READ | KEY_WRITE | KEY_WOW64_32KEY);
+ if (!dcc_key.Valid())
+ return false; // no DCC key.
+
+ dcc_key.DeleteValue(kDccValueName);
+
+ // Verify deletion.
+ wchar_t dcc[kMaxDccLength + 1];
+ DWORD dcc_size = arraysize(dcc);
+ if (dcc_key.ReadValue(kDccValueName, dcc, &dcc_size, NULL) == ERROR_SUCCESS) {
+ ASSERT_STRING("MachineDealCode::Clear: Could not delete the DCC value.");
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace rlz_lib
diff --git a/chromium/rlz/win/lib/machine_deal.h b/chromium/rlz/win/lib/machine_deal.h
new file mode 100644
index 00000000000..5b8b84a5ca2
--- /dev/null
+++ b/chromium/rlz/win/lib/machine_deal.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Library functions related to the OEM Deal Confirmation Code.
+
+#ifndef RLZ_WIN_LIB_MACHINE_DEAL_H_
+#define RLZ_WIN_LIB_MACHINE_DEAL_H_
+
+#include <string>
+#include "rlz/win/lib/rlz_lib.h"
+
+namespace rlz_lib {
+
+class MachineDealCode {
+ public:
+ // Set the OEM Deal Confirmation Code (DCC). This information is used for RLZ
+ // initalization. Must have write access to HKLM - SYSTEM or admin, unless
+ // rlz_lib::CreateMachineState() has been successfully called.
+ static bool Set(const char* dcc);
+
+ // Get the OEM Deal Confirmation Code from the registry. Used to ping
+ // the server.
+ static bool Get(AccessPoint point,
+ char* dcc,
+ int dcc_size,
+ const wchar_t* sid = NULL);
+
+ // Parses a ping response, checks if it is valid and sets the machine DCC
+ // from the response. The response should also contain the current value of
+ // the DCC to be considered valid.
+ // Write access to HKLM (system / admin) needed, unless
+ // rlz_lib::CreateMachineState() has been successfully called.
+ static bool SetFromPingResponse(const char* response);
+
+ // Gets the new DCC to set from a ping response. Returns true if the ping
+ // response is valid. Sets has_new_dcc true if there is a new DCC value.
+ static bool GetNewCodeFromPingResponse(const char* response,
+ bool* has_new_dcc,
+ char* new_dcc,
+ int new_dcc_size);
+
+ // Get the DCC cgi argument string to append to a daily or financial ping.
+ static bool GetAsCgi(char* cgi, int cgi_size);
+
+ // Get the machine code.
+ static bool Get(char* dcc, int dcc_size);
+
+ protected:
+ // Clear the DCC value. Only for testing purposes.
+ // Requires write access to HKLM, unless rlz_lib::CreateMachineState() has
+ // been successfully called.
+ static bool Clear();
+
+ MachineDealCode() {}
+ ~MachineDealCode() {}
+};
+
+} // namespace rlz_lib
+
+#endif // RLZ_WIN_LIB_MACHINE_DEAL_H_
diff --git a/chromium/rlz/win/lib/machine_deal_test.cc b/chromium/rlz/win/lib/machine_deal_test.cc
new file mode 100644
index 00000000000..47b72656c79
--- /dev/null
+++ b/chromium/rlz/win/lib/machine_deal_test.cc
@@ -0,0 +1,156 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A test application for the MachineDealCode class.
+//
+// These tests should not be executed on the build server:
+// - They assert for the failed cases.
+// - They modify machine state (registry).
+//
+// These tests require write access to HKLM and HKCU, unless
+// rlz_lib::CreateMachineState() has been successfully called.
+
+#include "base/logging.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "rlz/test/rlz_test_helpers.h"
+#include "rlz/win/lib/machine_deal.h"
+
+class MachineDealCodeHelper : public rlz_lib::MachineDealCode {
+ public:
+ static bool Clear() { return rlz_lib::MachineDealCode::Clear(); }
+
+ private:
+ MachineDealCodeHelper() {}
+ ~MachineDealCodeHelper() {}
+};
+
+class MachineDealCodeTest : public RlzLibTestBase {
+};
+
+TEST_F(MachineDealCodeTest, CreateMachineState) {
+ EXPECT_TRUE(rlz_lib::CreateMachineState());
+}
+
+TEST_F(MachineDealCodeTest, Set) {
+ MachineDealCodeHelper::Clear();
+ char dcc_50[50];
+ dcc_50[0] = 0;
+
+ EXPECT_TRUE(rlz_lib::MachineDealCode::Set("dcc_value"));
+
+ EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50));
+ EXPECT_STREQ("dcc_value", dcc_50);
+
+ EXPECT_TRUE(rlz_lib::MachineDealCode::Set("dcc_value_2"));
+
+ EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50));
+ EXPECT_STREQ("dcc_value_2", dcc_50);
+}
+
+TEST_F(MachineDealCodeTest, Get) {
+ MachineDealCodeHelper::Clear();
+ char dcc_50[50], dcc_2[2];
+ dcc_50[0] = 0;
+ dcc_2[0] = 0;
+
+ EXPECT_FALSE(rlz_lib::MachineDealCode::Get(dcc_50, 50));
+
+ EXPECT_TRUE(rlz_lib::MachineDealCode::Set("dcc_value"));
+
+ EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50));
+ EXPECT_STREQ("dcc_value", dcc_50);
+
+ EXPECT_FALSE(rlz_lib::MachineDealCode::Get(dcc_2, 2));
+}
+
+TEST_F(MachineDealCodeTest, SetFromPingResponse) {
+ rlz_lib::MachineDealCode::Set("MyDCCode");
+ char dcc_50[50];
+
+ // Bad responses
+
+ char* kBadDccResponse =
+ "dcc: NotMyDCCode \r\n"
+ "set_dcc: NewDCCode\r\n"
+ "crc32: 1B4D6BB3";
+ EXPECT_FALSE(rlz_lib::MachineDealCode::SetFromPingResponse(
+ kBadDccResponse));
+ EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50));
+ EXPECT_STREQ("MyDCCode", dcc_50);
+
+ char* kBadCrcResponse =
+ "dcc: MyDCCode \r\n"
+ "set_dcc: NewDCCode\r\n"
+ "crc32: 90707106";
+ EXPECT_FALSE(rlz_lib::MachineDealCode::SetFromPingResponse(
+ kBadCrcResponse));
+ EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50));
+ EXPECT_STREQ("MyDCCode", dcc_50);
+
+ // Good responses
+
+ char* kMissingSetResponse =
+ "dcc: MyDCCode \r\n"
+ "crc32: 35F2E717";
+ EXPECT_TRUE(rlz_lib::MachineDealCode::SetFromPingResponse(
+ kMissingSetResponse));
+ EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50));
+ EXPECT_STREQ("MyDCCode", dcc_50);
+
+ char* kGoodResponse =
+ "dcc: MyDCCode \r\n"
+ "set_dcc: NewDCCode\r\n"
+ "crc32: C8540E02";
+ EXPECT_TRUE(rlz_lib::MachineDealCode::SetFromPingResponse(
+ kGoodResponse));
+ EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50));
+ EXPECT_STREQ("NewDCCode", dcc_50);
+
+ char* kGoodResponse2 =
+ "set_dcc: NewDCCode2 \r\n"
+ "dcc: NewDCCode \r\n"
+ "crc32: 60B6409A";
+ EXPECT_TRUE(rlz_lib::MachineDealCode::SetFromPingResponse(
+ kGoodResponse2));
+ EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50));
+ EXPECT_STREQ("NewDCCode2", dcc_50);
+
+ MachineDealCodeHelper::Clear();
+ char* kGoodResponse3 =
+ "set_dcc: NewDCCode \r\n"
+ "crc32: 374C1C47";
+ EXPECT_TRUE(rlz_lib::MachineDealCode::SetFromPingResponse(
+ kGoodResponse3));
+ EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50));
+ EXPECT_STREQ("NewDCCode", dcc_50);
+
+ MachineDealCodeHelper::Clear();
+ char* kGoodResponse4 =
+ "dcc: \r\n"
+ "set_dcc: NewDCCode \r\n"
+ "crc32: 0AB1FB39";
+ EXPECT_TRUE(rlz_lib::MachineDealCode::SetFromPingResponse(
+ kGoodResponse4));
+ EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50));
+ EXPECT_STREQ("NewDCCode", dcc_50);
+}
+
+TEST_F(MachineDealCodeTest, GetAsCgi) {
+ MachineDealCodeHelper::Clear();
+ char cgi_50[50], cgi_2[2];
+ cgi_50[0] = 0;
+ cgi_2[0] = 0;
+
+ EXPECT_FALSE(rlz_lib::MachineDealCode::GetAsCgi(cgi_50, 50));
+ EXPECT_STREQ("", cgi_50);
+
+ EXPECT_TRUE(rlz_lib::MachineDealCode::Set("dcc_value"));
+
+ EXPECT_TRUE(rlz_lib::MachineDealCode::GetAsCgi(cgi_50, 50));
+ EXPECT_STREQ("dcc=dcc_value", cgi_50);
+
+ EXPECT_FALSE(rlz_lib::MachineDealCode::GetAsCgi(cgi_2, 2));
+}
diff --git a/chromium/rlz/win/lib/machine_id_win.cc b/chromium/rlz/win/lib/machine_id_win.cc
new file mode 100644
index 00000000000..245b753351f
--- /dev/null
+++ b/chromium/rlz/win/lib/machine_id_win.cc
@@ -0,0 +1,130 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+#include <Sddl.h> // For ConvertSidToStringSidW.
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "rlz/lib/assert.h"
+
+namespace rlz_lib {
+
+namespace {
+
+bool GetSystemVolumeSerialNumber(int* number) {
+ if (!number)
+ return false;
+
+ *number = 0;
+
+ // Find the system root path (e.g: C:\).
+ wchar_t system_path[MAX_PATH + 1];
+ if (!GetSystemDirectoryW(system_path, MAX_PATH))
+ return false;
+
+ wchar_t* first_slash = wcspbrk(system_path, L"\\/");
+ if (first_slash != NULL)
+ *(first_slash + 1) = 0;
+
+ DWORD number_local = 0;
+ if (!GetVolumeInformationW(system_path, NULL, 0, &number_local, NULL, NULL,
+ NULL, 0))
+ return false;
+
+ *number = number_local;
+ return true;
+}
+
+bool GetComputerSid(const wchar_t* account_name, SID* sid, DWORD sid_size) {
+ static const DWORD kStartDomainLength = 128; // reasonable to start with
+
+ scoped_ptr<wchar_t[]> domain_buffer(new wchar_t[kStartDomainLength]);
+ DWORD domain_size = kStartDomainLength;
+ DWORD sid_dword_size = sid_size;
+ SID_NAME_USE sid_name_use;
+
+ BOOL success = ::LookupAccountNameW(NULL, account_name, sid,
+ &sid_dword_size, domain_buffer.get(),
+ &domain_size, &sid_name_use);
+ if (!success && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ // We could have gotten the insufficient buffer error because
+ // one or both of sid and szDomain was too small. Check for that
+ // here.
+ if (sid_dword_size > sid_size)
+ return false;
+
+ if (domain_size > kStartDomainLength)
+ domain_buffer.reset(new wchar_t[domain_size]);
+
+ success = ::LookupAccountNameW(NULL, account_name, sid, &sid_dword_size,
+ domain_buffer.get(), &domain_size,
+ &sid_name_use);
+ }
+
+ return success != FALSE;
+}
+
+std::wstring ConvertSidToString(SID* sid) {
+ std::wstring sid_string;
+#if _WIN32_WINNT >= 0x500
+ wchar_t* sid_buffer = NULL;
+ if (ConvertSidToStringSidW(sid, &sid_buffer)) {
+ sid_string = sid_buffer;
+ LocalFree(sid_buffer);
+ }
+#else
+ SID_IDENTIFIER_AUTHORITY* sia = ::GetSidIdentifierAuthority(sid);
+
+ if(sia->Value[0] || sia->Value[1]) {
+ base::SStringPrintf(
+ &sid_string, L"S-%d-0x%02hx%02hx%02hx%02hx%02hx%02hx",
+ SID_REVISION, (USHORT)sia->Value[0], (USHORT)sia->Value[1],
+ (USHORT)sia->Value[2], (USHORT)sia->Value[3], (USHORT)sia->Value[4],
+ (USHORT)sia->Value[5]);
+ } else {
+ ULONG authority = 0;
+ for (int i = 2; i < 6; ++i) {
+ authority <<= 8;
+ authority |= sia->Value[i];
+ }
+ base::SStringPrintf(&sid_string, L"S-%d-%lu", SID_REVISION, authority);
+ }
+
+ int sub_auth_count = *::GetSidSubAuthorityCount(sid);
+ for(int i = 0; i < sub_auth_count; ++i)
+ base::StringAppendF(&sid_string, L"-%lu", *::GetSidSubAuthority(sid, i));
+#endif
+
+ return sid_string;
+}
+
+} // namespace
+
+bool GetRawMachineId(string16* sid_string, int* volume_id) {
+ // Calculate the Windows SID.
+
+ wchar_t computer_name[MAX_COMPUTERNAME_LENGTH + 1] = {0};
+ DWORD size = arraysize(computer_name);
+
+ if (GetComputerNameW(computer_name, &size)) {
+ char sid_buffer[SECURITY_MAX_SID_SIZE];
+ SID* sid = reinterpret_cast<SID*>(sid_buffer);
+ if (GetComputerSid(computer_name, sid, SECURITY_MAX_SID_SIZE)) {
+ *sid_string = ConvertSidToString(sid);
+ }
+ }
+
+ // Get the system drive volume serial number.
+ *volume_id = 0;
+ if (!GetSystemVolumeSerialNumber(volume_id)) {
+ ASSERT_STRING("GetMachineId: Failed to retrieve volume serial number");
+ *volume_id = 0;
+ }
+
+ return true;
+}
+
+} // namespace rlz_lib
diff --git a/chromium/rlz/win/lib/process_info.cc b/chromium/rlz/win/lib/process_info.cc
new file mode 100644
index 00000000000..2511f54ab76
--- /dev/null
+++ b/chromium/rlz/win/lib/process_info.cc
@@ -0,0 +1,195 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Information about the current process.
+
+#include "rlz/win/lib/process_info.h"
+
+#include <windows.h>
+#include <Sddl.h> // For ConvertSidToStringSid.
+#include <LMCons.h> // For UNLEN
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/process/process_handle.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+#include "rlz/lib/assert.h"
+
+namespace {
+
+HRESULT GetCurrentUser(std::wstring* name,
+ std::wstring* domain,
+ std::wstring* sid) {
+ DWORD err;
+
+ // Get the current username & domain the hard way. (GetUserNameEx would be
+ // nice, but unfortunately requires connectivity to a domain controller.
+ // Useless.)
+
+ // (Following call doesn't work if running as a Service - because a Service
+ // runs under special accounts like LOCAL_SYSTEM, not as the logged in user.
+ // In which case, search for and use the process handle of a running
+ // Explorer.exe.)
+ HANDLE token;
+ if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token))
+ return E_FAIL;
+
+ base::win::ScopedHandle scoped_process_token(token);
+
+ // (Following call will fail with ERROR_INSUFFICIENT_BUFFER and give us the
+ // required size.)
+ scoped_ptr<char[]> token_user_bytes;
+ DWORD token_user_size;
+ DWORD token_user_size2;
+ BOOL result = ::GetTokenInformation(token, TokenUser, NULL, 0,
+ &token_user_size);
+ err = ::GetLastError();
+ CHECK(!result && err == ERROR_INSUFFICIENT_BUFFER);
+
+ token_user_bytes.reset(new char[token_user_size]);
+ if (!token_user_bytes.get())
+ return E_OUTOFMEMORY;
+
+ if (!::GetTokenInformation(token, TokenUser, token_user_bytes.get(),
+ token_user_size, &token_user_size2)) {
+ return E_FAIL;
+ }
+
+ WCHAR user_name[UNLEN + 1]; // max username length
+ WCHAR domain_name[UNLEN + 1];
+ DWORD user_name_size = UNLEN + 1;
+ DWORD domain_name_size = UNLEN + 1;
+ SID_NAME_USE sid_type;
+ TOKEN_USER* token_user =
+ reinterpret_cast<TOKEN_USER*>(token_user_bytes.get());
+ if (!token_user)
+ return E_FAIL;
+ PSID user_sid = token_user->User.Sid;
+ if (!::LookupAccountSidW(NULL, user_sid, user_name, &user_name_size,
+ domain_name, &domain_name_size, &sid_type)) {
+ return E_FAIL;
+ }
+
+ if (name != NULL) {
+ *name = user_name;
+ }
+ if (domain != NULL) {
+ *domain = domain_name;
+ }
+ if (sid != NULL) {
+ LPWSTR string_sid;
+ ConvertSidToStringSidW(user_sid, &string_sid);
+ *sid = string_sid; // copy out to cstring
+ // free memory, as documented for ConvertSidToStringSid
+ LocalFree(string_sid);
+ }
+
+ return S_OK;
+}
+
+HRESULT GetElevationType(PTOKEN_ELEVATION_TYPE elevation) {
+ if (!elevation)
+ return E_POINTER;
+
+ *elevation = TokenElevationTypeDefault;
+
+ if (base::win::GetVersion() < base::win::VERSION_VISTA)
+ return E_FAIL;
+
+ HANDLE process_token;
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &process_token))
+ return HRESULT_FROM_WIN32(GetLastError());
+
+ base::win::ScopedHandle scoped_process_token(process_token);
+
+ DWORD size;
+ TOKEN_ELEVATION_TYPE elevation_type;
+ if (!GetTokenInformation(process_token, TokenElevationType, &elevation_type,
+ sizeof(elevation_type), &size)) {
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+
+ *elevation = elevation_type;
+ return S_OK;
+}
+
+// based on http://msdn2.microsoft.com/en-us/library/aa376389.aspx
+bool GetUserGroup(long* group) {
+ if (!group)
+ return false;
+
+ *group = 0;
+
+ // groups are listed in DECREASING order of importance
+ // (eg. If a user is a member of both the admin group and
+ // the power user group, it is more useful to list the user
+ // as an admin)
+ DWORD user_groups[] = {DOMAIN_ALIAS_RID_ADMINS,
+ DOMAIN_ALIAS_RID_POWER_USERS};
+ SID_IDENTIFIER_AUTHORITY nt_authority = SECURITY_NT_AUTHORITY;
+
+ for (int i = 0; i < arraysize(user_groups) && *group == 0; ++i) {
+ PSID current_group;
+ if (AllocateAndInitializeSid(&nt_authority, 2,
+ SECURITY_BUILTIN_DOMAIN_RID,
+ user_groups[i], 0, 0, 0, 0,
+ 0, 0, &current_group)) {
+ BOOL current_level;
+ if (CheckTokenMembership(NULL, current_group, &current_level) &&
+ current_level) {
+ *group = user_groups[i];
+ }
+
+ FreeSid(current_group);
+ }
+ }
+
+ return group != 0;
+}
+} //anonymous
+
+
+namespace rlz_lib {
+
+bool ProcessInfo::IsRunningAsSystem() {
+ static std::wstring name;
+ static std::wstring domain;
+ static std::wstring sid;
+ if (name.empty())
+ CHECK(SUCCEEDED(GetCurrentUser(&name, &domain, &sid)));
+
+ return (name == L"SYSTEM");
+}
+
+bool ProcessInfo::HasAdminRights() {
+ static bool evaluated = false;
+ static bool has_rights = false;
+
+ if (!evaluated) {
+ if (IsRunningAsSystem()) {
+ has_rights = true;
+ } else if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
+ TOKEN_ELEVATION_TYPE elevation;
+ base::IntegrityLevel level;
+
+ if (SUCCEEDED(GetElevationType(&elevation)) &&
+ base::GetProcessIntegrityLevel(base::GetCurrentProcessHandle(), &level))
+ has_rights = (elevation == TokenElevationTypeFull) ||
+ (level == HIGH_INTEGRITY);
+ } else {
+ long group = 0;
+ if (GetUserGroup(&group))
+ has_rights = (group == DOMAIN_ALIAS_RID_ADMINS);
+ }
+ }
+
+ evaluated = true;
+ if (!has_rights)
+ ASSERT_STRING("ProcessInfo::HasAdminRights: Does not have admin rights.");
+
+ return has_rights;
+}
+
+}; // namespace
diff --git a/chromium/rlz/win/lib/process_info.h b/chromium/rlz/win/lib/process_info.h
new file mode 100644
index 00000000000..84bd6048245
--- /dev/null
+++ b/chromium/rlz/win/lib/process_info.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Information about the current process.
+
+#ifndef RLZ_WIN_LIB_PROCESS_INFO_H_
+#define RLZ_WIN_LIB_PROCESS_INFO_H_
+
+#include "base/basictypes.h"
+
+namespace rlz_lib {
+
+class ProcessInfo {
+ public:
+ enum IntegrityLevel {
+ INTEGRITY_UNKNOWN,
+ LOW_INTEGRITY,
+ MEDIUM_INTEGRITY,
+ HIGH_INTEGRITY,
+ };
+
+ // All these functions cache the result after first run.
+ static bool IsRunningAsSystem();
+ static bool HasAdminRights(); // System / Admin / High Elevation on Vista
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProcessInfo);
+}; // class
+}; // namespace
+
+#endif // RLZ_WIN_LIB_PROCESS_INFO_H_
diff --git a/chromium/rlz/win/lib/registry_util.cc b/chromium/rlz/win/lib/registry_util.cc
new file mode 100644
index 00000000000..cb4a6868fae
--- /dev/null
+++ b/chromium/rlz/win/lib/registry_util.cc
@@ -0,0 +1,77 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A helper library to keep track of a user's key by SID.
+// Used by RLZ libary. Also to be used by SearchWithGoogle library.
+
+#include "rlz/win/lib/registry_util.h"
+
+#include "base/process/process_handle.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/win/registry.h"
+#include "base/win/windows_version.h"
+#include "rlz/lib/assert.h"
+#include "rlz/win/lib/process_info.h"
+
+namespace rlz_lib {
+
+bool RegKeyReadValue(base::win::RegKey& key, const wchar_t* name,
+ char* value, size_t* value_size) {
+ value[0] = 0;
+
+ std::wstring value_string;
+ if (key.ReadValue(name, &value_string) != ERROR_SUCCESS) {
+ return false;
+ }
+
+ if (value_string.length() > *value_size) {
+ *value_size = value_string.length();
+ return false;
+ }
+
+ // Note that RLZ string are always ASCII by design.
+ strncpy(value, WideToUTF8(value_string).c_str(), *value_size);
+ value[*value_size - 1] = 0;
+ return true;
+}
+
+bool RegKeyWriteValue(base::win::RegKey& key, const wchar_t* name,
+ const char* value) {
+ std::wstring value_string(ASCIIToWide(value));
+ return key.WriteValue(name, value_string.c_str()) == ERROR_SUCCESS;
+}
+
+bool HasUserKeyAccess(bool write_access) {
+ // The caller is trying to access HKEY_CURRENT_USER. Test to see if we can
+ // read from there. Don't try HKEY_CURRENT_USER because this will cause
+ // problems in the unit tests: if we open HKEY_CURRENT_USER directly here,
+ // the overriding done for unit tests will no longer work. So we try subkey
+ // "Software" which is known to always exist.
+ base::win::RegKey key;
+ if (key.Open(HKEY_CURRENT_USER, L"Software", KEY_READ) != ERROR_SUCCESS)
+ ASSERT_STRING("Could not open HKEY_CURRENT_USER");
+
+ if (ProcessInfo::IsRunningAsSystem()) {
+ ASSERT_STRING("UserKey::HasAccess: No access as SYSTEM without SID set.");
+ return false;
+ }
+
+ if (write_access) {
+ if (base::win::GetVersion() < base::win::VERSION_VISTA) return true;
+ base::ProcessHandle process_handle = base::GetCurrentProcessHandle();
+ base::IntegrityLevel level = base::INTEGRITY_UNKNOWN;
+
+ if (!base::GetProcessIntegrityLevel(process_handle, &level)) {
+ ASSERT_STRING("UserKey::HasAccess: Cannot determine Integrity Level.");
+ return false;
+ }
+ if (level <= base::LOW_INTEGRITY) {
+ ASSERT_STRING("UserKey::HasAccess: Cannot write from Low Integrity.");
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace rlz_lib
diff --git a/chromium/rlz/win/lib/registry_util.h b/chromium/rlz/win/lib/registry_util.h
new file mode 100644
index 00000000000..60b8be6e6a4
--- /dev/null
+++ b/chromium/rlz/win/lib/registry_util.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RLZ_WIN_LIB_REGISTRY_UTIL_H_
+#define RLZ_WIN_LIB_REGISTRY_UTIL_H_
+
+namespace base {
+namespace win {
+class RegKey;
+} // namespace win
+} // namespace base
+
+namespace rlz_lib {
+
+bool RegKeyReadValue(base::win::RegKey& key,
+ const wchar_t* name,
+ char* value,
+ size_t* value_size);
+
+bool RegKeyWriteValue(base::win::RegKey& key,
+ const wchar_t* name,
+ const char* value);
+
+bool HasUserKeyAccess(bool write_access);
+
+} // namespace rlz_lib
+
+#endif // RLZ_WIN_LIB_REGISTRY_UTIL_H_
diff --git a/chromium/rlz/win/lib/rlz_lib.h b/chromium/rlz/win/lib/rlz_lib.h
new file mode 100644
index 00000000000..3adbf97b433
--- /dev/null
+++ b/chromium/rlz/win/lib/rlz_lib.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A library to manage RLZ information for access-points shared
+// across different client applications.
+//
+// All functions return true on success and false on error.
+// This implemenation is thread safe.
+//
+// Each prototype mentions the registry access requirements:
+//
+// HKLM read: Will work from any process and at any privilege level on Vista.
+// HKCU read: Calls made from the SYSTEM account must pass the current user's
+// SID as the optional 'sid' param. Can be called from low integrity
+// process on Vista.
+// HKCU write: Calls made from the SYSTEM account must pass the current user's
+// SID as the optional 'sid' param. Calls require at least medium
+// integrity on Vista (e.g. Toolbar will need to use their broker)
+// HKLM write: Calls must be made from an account with admin rights. No SID
+// need be passed when running as SYSTEM.
+// Functions which do not access registry will be marked with "no restrictions".
+
+#ifndef RLZ_WIN_LIB_RLZ_LIB_H_
+#define RLZ_WIN_LIB_RLZ_LIB_H_
+
+// Clients can get away by just including rlz/lib/rlz_lib.h. This file only
+// contains function definitions for files used by tests. It's mostly kept
+// around for backwards-compatibility.
+
+#include "rlz/lib/rlz_lib.h"
+
+#include "base/win/registry.h"
+
+namespace rlz_lib {
+
+#if defined(OS_WIN)
+
+// Initialize temporary HKLM/HKCU registry hives used for testing.
+// Testing RLZ requires reading and writing to the Windows registry. To keep
+// the tests isolated from the machine's state, as well as to prevent the tests
+// from causing side effects in the registry, HKCU and HKLM are overridden for
+// the duration of the tests. RLZ tests don't expect the HKCU and KHLM hives to
+// be empty though, and this function initializes the minimum value needed so
+// that the test will run successfully.
+//
+// The two arguments to this function should be the keys that will represent
+// the HKLM and HKCU registry hives during the tests. This function should be
+// called *before* the hives are overridden.
+void InitializeTempHivesForTesting(const base::win::RegKey& temp_hklm_key,
+ const base::win::RegKey& temp_hkcu_key);
+#endif // defined(OS_WIN)
+
+} // namespace rlz_lib
+
+#endif // RLZ_WIN_LIB_RLZ_LIB_H_
diff --git a/chromium/rlz/win/lib/rlz_lib_win.cc b/chromium/rlz/win/lib/rlz_lib_win.cc
new file mode 100644
index 00000000000..d8b1c1e9fee
--- /dev/null
+++ b/chromium/rlz/win/lib/rlz_lib_win.cc
@@ -0,0 +1,260 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A library to manage RLZ information for access-points shared
+// across different client applications.
+
+#include "rlz/win/lib/rlz_lib.h"
+
+#include <windows.h>
+#include <aclapi.h>
+#include <winerror.h>
+
+#include "base/basictypes.h"
+#include "base/win/registry.h"
+#include "base/win/windows_version.h"
+#include "rlz/lib/assert.h"
+#include "rlz/lib/rlz_value_store.h"
+#include "rlz/win/lib/machine_deal.h"
+#include "rlz/win/lib/rlz_value_store_registry.h"
+
+namespace {
+
+// Path to recursively copy into the replacemment hives. These are needed
+// to make sure certain win32 APIs continue to run correctly once the real
+// hives are replaced.
+const wchar_t* kHKLMAccessProviders =
+ L"System\\CurrentControlSet\\Control\\Lsa\\AccessProviders";
+
+// Helper functions
+
+void CopyRegistryTree(const base::win::RegKey& src, base::win::RegKey* dest) {
+ // First copy values.
+ for (base::win::RegistryValueIterator i(src.Handle(), L"");
+ i.Valid(); ++i) {
+ dest->WriteValue(i.Name(), reinterpret_cast<const void*>(i.Value()),
+ i.ValueSize(), i.Type());
+ }
+
+ // Next copy subkeys recursively.
+ for (base::win::RegistryKeyIterator i(src.Handle(), L"");
+ i.Valid(); ++i) {
+ base::win::RegKey subkey(dest->Handle(), i.Name(), KEY_ALL_ACCESS);
+ CopyRegistryTree(base::win::RegKey(src.Handle(), i.Name(), KEY_READ),
+ &subkey);
+ }
+}
+
+} // namespace anonymous
+
+
+namespace rlz_lib {
+
+// OEM Deal confirmation storage functions.
+
+template<class T>
+class typed_buffer_ptr {
+ scoped_ptr<char[]> buffer_;
+
+ public:
+ typed_buffer_ptr() {
+ }
+
+ explicit typed_buffer_ptr(size_t size) : buffer_(new char[size]) {
+ }
+
+ void reset(size_t size) {
+ buffer_.reset(new char[size]);
+ }
+
+ operator T*() {
+ return reinterpret_cast<T*>(buffer_.get());
+ }
+};
+
+// Check if this SID has the desired access by scanning the ACEs in the DACL.
+// This function is part of the rlz_lib namespace so that it can be called from
+// unit tests. Non-unit test code should not call this function.
+bool HasAccess(PSID sid, ACCESS_MASK access_mask, ACL* dacl) {
+ if (dacl == NULL)
+ return false;
+
+ ACL_SIZE_INFORMATION info;
+ if (!GetAclInformation(dacl, &info, sizeof(info), AclSizeInformation))
+ return false;
+
+ GENERIC_MAPPING generic_mapping = {KEY_READ, KEY_WRITE, KEY_EXECUTE,
+ KEY_ALL_ACCESS};
+ MapGenericMask(&access_mask, &generic_mapping);
+
+ for (DWORD i = 0; i < info.AceCount; ++i) {
+ ACCESS_ALLOWED_ACE* ace;
+ if (GetAce(dacl, i, reinterpret_cast<void**>(&ace))) {
+ if ((ace->Header.AceFlags & INHERIT_ONLY_ACE) == INHERIT_ONLY_ACE)
+ continue;
+
+ PSID existing_sid = reinterpret_cast<PSID>(&ace->SidStart);
+ DWORD mask = ace->Mask;
+ MapGenericMask(&mask, &generic_mapping);
+
+ if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE &&
+ (mask & access_mask) == access_mask && EqualSid(existing_sid, sid))
+ return true;
+
+ if (ace->Header.AceType == ACCESS_DENIED_ACE_TYPE &&
+ (mask & access_mask) != 0 && EqualSid(existing_sid, sid))
+ return false;
+ }
+ }
+
+ return false;
+}
+
+bool CreateMachineState() {
+ LibMutex lock;
+ if (lock.failed())
+ return false;
+
+ base::win::RegKey hklm_key;
+ if (hklm_key.Create(HKEY_LOCAL_MACHINE,
+ RlzValueStoreRegistry::GetWideLibKeyName().c_str(),
+ KEY_ALL_ACCESS | KEY_WOW64_32KEY) != ERROR_SUCCESS) {
+ ASSERT_STRING("rlz_lib::CreateMachineState: "
+ "Unable to create / open machine key.");
+ return false;
+ }
+
+ // Create a SID that represents ALL USERS.
+ DWORD users_sid_size = SECURITY_MAX_SID_SIZE;
+ typed_buffer_ptr<SID> users_sid(users_sid_size);
+ CreateWellKnownSid(WinBuiltinUsersSid, NULL, users_sid, &users_sid_size);
+
+ // Get the security descriptor for the registry key.
+ DWORD original_sd_size = 0;
+ ::RegGetKeySecurity(hklm_key.Handle(), DACL_SECURITY_INFORMATION, NULL,
+ &original_sd_size);
+ typed_buffer_ptr<SECURITY_DESCRIPTOR> original_sd(original_sd_size);
+
+ LONG result = ::RegGetKeySecurity(hklm_key.Handle(),
+ DACL_SECURITY_INFORMATION, original_sd, &original_sd_size);
+ if (result != ERROR_SUCCESS) {
+ ASSERT_STRING("rlz_lib::CreateMachineState: "
+ "Unable to create / open machine key.");
+ return false;
+ }
+
+ // Make a copy of the security descriptor so we can modify it. The one
+ // returned by RegGetKeySecurity() is self-relative, so we need to make it
+ // absolute.
+ DWORD new_sd_size = 0;
+ DWORD dacl_size = 0;
+ DWORD sacl_size = 0;
+ DWORD owner_size = 0;
+ DWORD group_size = 0;
+ ::MakeAbsoluteSD(original_sd, NULL, &new_sd_size, NULL, &dacl_size,
+ NULL, &sacl_size, NULL, &owner_size,
+ NULL, &group_size);
+
+ typed_buffer_ptr<SECURITY_DESCRIPTOR> new_sd(new_sd_size);
+ // Make sure the DACL is big enough to add one more ACE.
+ typed_buffer_ptr<ACL> dacl(dacl_size + SECURITY_MAX_SID_SIZE);
+ typed_buffer_ptr<ACL> sacl(sacl_size);
+ typed_buffer_ptr<SID> owner(owner_size);
+ typed_buffer_ptr<SID> group(group_size);
+
+ if (!::MakeAbsoluteSD(original_sd, new_sd, &new_sd_size, dacl, &dacl_size,
+ sacl, &sacl_size, owner, &owner_size,
+ group, &group_size)) {
+ ASSERT_STRING("rlz_lib::CreateMachineState: MakeAbsoluteSD failed");
+ return false;
+ }
+
+ // If all users already have read/write access to the registry key, then
+ // nothing to do. Otherwise change the security descriptor of the key to
+ // give everyone access.
+ if (HasAccess(users_sid, KEY_ALL_ACCESS, dacl)) {
+ return false;
+ }
+
+ // Add ALL-USERS ALL-ACCESS ACL.
+ EXPLICIT_ACCESS ea;
+ ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
+ ea.grfAccessPermissions = GENERIC_ALL | KEY_ALL_ACCESS;
+ ea.grfAccessMode = GRANT_ACCESS;
+ ea.grfInheritance= SUB_CONTAINERS_AND_OBJECTS_INHERIT;
+ ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
+ ea.Trustee.ptstrName = L"Everyone";
+
+ ACL* new_dacl = NULL;
+ result = SetEntriesInAcl(1, &ea, dacl, &new_dacl);
+ if (result != ERROR_SUCCESS) {
+ ASSERT_STRING("rlz_lib::CreateMachineState: SetEntriesInAcl failed");
+ return false;
+ }
+
+ BOOL ok = SetSecurityDescriptorDacl(new_sd, TRUE, new_dacl, FALSE);
+ if (!ok) {
+ ASSERT_STRING("rlz_lib::CreateMachineState: "
+ "SetSecurityDescriptorOwner failed");
+ LocalFree(new_dacl);
+ return false;
+ }
+
+ result = ::RegSetKeySecurity(hklm_key.Handle(),
+ DACL_SECURITY_INFORMATION,
+ new_sd);
+ // Note that the new DACL cannot be freed until after the call to
+ // RegSetKeySecurity().
+ LocalFree(new_dacl);
+
+ bool success = true;
+ if (result != ERROR_SUCCESS) {
+ ASSERT_STRING("rlz_lib::CreateMachineState: "
+ "Unable to create / open machine key.");
+ success = false;
+ }
+
+
+ return success;
+}
+
+bool SetMachineDealCode(const char* dcc) {
+ return MachineDealCode::Set(dcc);
+}
+
+bool GetMachineDealCodeAsCgi(char* cgi, size_t cgi_size) {
+ return MachineDealCode::GetAsCgi(cgi, cgi_size);
+}
+
+bool GetMachineDealCode(char* dcc, size_t dcc_size) {
+ return MachineDealCode::Get(dcc, dcc_size);
+}
+
+// Combined functions.
+
+bool SetMachineDealCodeFromPingResponse(const char* response) {
+ return MachineDealCode::SetFromPingResponse(response);
+}
+
+void InitializeTempHivesForTesting(const base::win::RegKey& temp_hklm_key,
+ const base::win::RegKey& temp_hkcu_key) {
+ // For the moment, the HKCU hive requires no initialization.
+
+ if (base::win::GetVersion() >= base::win::VERSION_WIN7) {
+ // Copy the following HKLM subtrees to the temporary location so that the
+ // win32 APIs used by the tests continue to work:
+ //
+ // HKLM\System\CurrentControlSet\Control\Lsa\AccessProviders
+ //
+ // This seems to be required since Win7.
+ base::win::RegKey dest(temp_hklm_key.Handle(), kHKLMAccessProviders,
+ KEY_ALL_ACCESS);
+ CopyRegistryTree(base::win::RegKey(HKEY_LOCAL_MACHINE,
+ kHKLMAccessProviders,
+ KEY_READ),
+ &dest);
+ }
+}
+
+} // namespace rlz_lib
diff --git a/chromium/rlz/win/lib/rlz_value_store_registry.cc b/chromium/rlz/win/lib/rlz_value_store_registry.cc
new file mode 100644
index 00000000000..0aa0023c0cb
--- /dev/null
+++ b/chromium/rlz/win/lib/rlz_value_store_registry.cc
@@ -0,0 +1,384 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "rlz/win/lib/rlz_value_store_registry.h"
+
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/win/registry.h"
+#include "rlz/lib/assert.h"
+#include "rlz/lib/lib_values.h"
+#include "rlz/lib/rlz_lib.h"
+#include "rlz/lib/string_utils.h"
+#include "rlz/win/lib/registry_util.h"
+
+namespace rlz_lib {
+
+namespace {
+
+//
+// Registry keys:
+//
+// RLZ's are stored as:
+// <AccessPointName> = <RLZ value> @ kRootKey\kLibKeyName\kRlzsSubkeyName.
+//
+// Events are stored as:
+// <AccessPointName><EventName> = 1 @
+// HKCU\kLibKeyName\kEventsSubkeyName\GetProductName(product).
+//
+// The OEM Deal Confirmation Code (DCC) is stored as
+// kDccValueName = <DCC value> @ HKLM\kLibKeyName
+//
+// The last ping time, per product is stored as:
+// GetProductName(product) = <last ping time> @
+// HKCU\kLibKeyName\kPingTimesSubkeyName.
+//
+// The server does not care about any of these constants.
+//
+const char kLibKeyName[] = "Software\\Google\\Common\\Rlz";
+const wchar_t kGoogleKeyName[] = L"Software\\Google";
+const wchar_t kGoogleCommonKeyName[] = L"Software\\Google\\Common";
+const char kRlzsSubkeyName[] = "RLZs";
+const char kEventsSubkeyName[] = "Events";
+const char kStatefulEventsSubkeyName[] = "StatefulEvents";
+const char kPingTimesSubkeyName[] = "PTimes";
+
+std::wstring GetWideProductName(Product product) {
+ return ASCIIToWide(GetProductName(product));
+}
+
+void AppendBrandToString(std::string* str) {
+ std::string brand(SupplementaryBranding::GetBrand());
+ if (!brand.empty())
+ base::StringAppendF(str, "\\_%s", brand.c_str());
+}
+
+// Function to get the specific registry keys.
+bool GetRegKey(const char* name, REGSAM access, base::win::RegKey* key) {
+ std::string key_location;
+ base::StringAppendF(&key_location, "%s\\%s", kLibKeyName, name);
+ AppendBrandToString(&key_location);
+
+ LONG ret = ERROR_SUCCESS;
+ if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK)) {
+ ret = key->Create(HKEY_CURRENT_USER, ASCIIToWide(key_location).c_str(),
+ access);
+ } else {
+ ret = key->Open(HKEY_CURRENT_USER, ASCIIToWide(key_location).c_str(),
+ access);
+ }
+
+ return ret == ERROR_SUCCESS;
+}
+
+bool GetPingTimesRegKey(REGSAM access, base::win::RegKey* key) {
+ return GetRegKey(kPingTimesSubkeyName, access, key);
+}
+
+
+bool GetEventsRegKey(const char* event_type,
+ const rlz_lib::Product* product,
+ REGSAM access, base::win::RegKey* key) {
+ std::string key_location;
+ base::StringAppendF(&key_location, "%s\\%s", kLibKeyName,
+ event_type);
+ AppendBrandToString(&key_location);
+
+ if (product != NULL) {
+ std::string product_name = GetProductName(*product);
+ if (product_name.empty())
+ return false;
+
+ base::StringAppendF(&key_location, "\\%s", product_name.c_str());
+ }
+
+ LONG ret = ERROR_SUCCESS;
+ if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK)) {
+ ret = key->Create(HKEY_CURRENT_USER, ASCIIToWide(key_location).c_str(),
+ access);
+ } else {
+ ret = key->Open(HKEY_CURRENT_USER, ASCIIToWide(key_location).c_str(),
+ access);
+ }
+
+ return ret == ERROR_SUCCESS;
+}
+
+bool GetAccessPointRlzsRegKey(REGSAM access, base::win::RegKey* key) {
+ return GetRegKey(kRlzsSubkeyName, access, key);
+}
+
+bool ClearAllProductEventValues(rlz_lib::Product product, const char* key) {
+ std::wstring product_name = GetWideProductName(product);
+ if (product_name.empty())
+ return false;
+
+ base::win::RegKey reg_key;
+ GetEventsRegKey(key, NULL, KEY_WRITE, &reg_key);
+ reg_key.DeleteKey(product_name.c_str());
+
+ // Verify that the value no longer exists.
+ base::win::RegKey product_events(
+ reg_key.Handle(), product_name.c_str(), KEY_READ);
+ if (product_events.Valid()) {
+ ASSERT_STRING("ClearAllProductEvents: Key deletion failed");
+ return false;
+ }
+
+ return true;
+}
+
+// Deletes a registry key if it exists and has no subkeys or values.
+// TODO: Move this to a registry_utils file and add unittest.
+bool DeleteKeyIfEmpty(HKEY root_key, const wchar_t* key_name) {
+ if (!key_name) {
+ ASSERT_STRING("DeleteKeyIfEmpty: key_name is NULL");
+ return false;
+ } else { // Scope needed for RegKey
+ base::win::RegKey key(root_key, key_name, KEY_READ);
+ if (!key.Valid())
+ return true; // Key does not exist - nothing to do.
+
+ base::win::RegistryKeyIterator key_iter(root_key, key_name);
+ if (key_iter.SubkeyCount() > 0)
+ return true; // Not empty, so nothing to do
+
+ base::win::RegistryValueIterator value_iter(root_key, key_name);
+ if (value_iter.ValueCount() > 0)
+ return true; // Not empty, so nothing to do
+ }
+
+ // The key is empty - delete it now.
+ base::win::RegKey key(root_key, L"", KEY_WRITE);
+ return key.DeleteKey(key_name) == ERROR_SUCCESS;
+}
+
+} // namespace
+
+// static
+std::wstring RlzValueStoreRegistry::GetWideLibKeyName() {
+ return ASCIIToWide(kLibKeyName);
+}
+
+bool RlzValueStoreRegistry::HasAccess(AccessType type) {
+ return HasUserKeyAccess(type == kWriteAccess);
+}
+
+bool RlzValueStoreRegistry::WritePingTime(Product product, int64 time) {
+ base::win::RegKey key;
+ std::wstring product_name = GetWideProductName(product);
+ return GetPingTimesRegKey(KEY_WRITE, &key) &&
+ key.WriteValue(product_name.c_str(), &time, sizeof(time),
+ REG_QWORD) == ERROR_SUCCESS;
+}
+
+bool RlzValueStoreRegistry::ReadPingTime(Product product, int64* time) {
+ base::win::RegKey key;
+ std::wstring product_name = GetWideProductName(product);
+ return GetPingTimesRegKey(KEY_READ, &key) &&
+ key.ReadInt64(product_name.c_str(), time) == ERROR_SUCCESS;
+}
+
+bool RlzValueStoreRegistry::ClearPingTime(Product product) {
+ base::win::RegKey key;
+ GetPingTimesRegKey(KEY_WRITE, &key);
+
+ std::wstring product_name = GetWideProductName(product);
+ key.DeleteValue(product_name.c_str());
+
+ // Verify deletion.
+ uint64 value;
+ DWORD size = sizeof(value);
+ if (key.ReadValue(
+ product_name.c_str(), &value, &size, NULL) == ERROR_SUCCESS) {
+ ASSERT_STRING("RlzValueStoreRegistry::ClearPingTime: Failed to delete.");
+ return false;
+ }
+
+ return true;
+}
+
+bool RlzValueStoreRegistry::WriteAccessPointRlz(AccessPoint access_point,
+ const char* new_rlz) {
+ const char* access_point_name = GetAccessPointName(access_point);
+ if (!access_point_name)
+ return false;
+
+ std::wstring access_point_name_wide(ASCIIToWide(access_point_name));
+ base::win::RegKey key;
+ GetAccessPointRlzsRegKey(KEY_WRITE, &key);
+
+ if (!RegKeyWriteValue(key, access_point_name_wide.c_str(), new_rlz)) {
+ ASSERT_STRING("SetAccessPointRlz: Could not write the new RLZ value");
+ return false;
+ }
+ return true;
+}
+
+bool RlzValueStoreRegistry::ReadAccessPointRlz(AccessPoint access_point,
+ char* rlz,
+ size_t rlz_size) {
+ const char* access_point_name = GetAccessPointName(access_point);
+ if (!access_point_name)
+ return false;
+
+ size_t size = rlz_size;
+ base::win::RegKey key;
+ GetAccessPointRlzsRegKey(KEY_READ, &key);
+ if (!RegKeyReadValue(key, ASCIIToWide(access_point_name).c_str(),
+ rlz, &size)) {
+ rlz[0] = 0;
+ if (size > rlz_size) {
+ ASSERT_STRING("GetAccessPointRlz: Insufficient buffer size");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool RlzValueStoreRegistry::ClearAccessPointRlz(AccessPoint access_point) {
+ const char* access_point_name = GetAccessPointName(access_point);
+ if (!access_point_name)
+ return false;
+
+ std::wstring access_point_name_wide(ASCIIToWide(access_point_name));
+ base::win::RegKey key;
+ GetAccessPointRlzsRegKey(KEY_WRITE, &key);
+
+ key.DeleteValue(access_point_name_wide.c_str());
+
+ // Verify deletion.
+ DWORD value;
+ if (key.ReadValueDW(access_point_name_wide.c_str(), &value) ==
+ ERROR_SUCCESS) {
+ ASSERT_STRING("SetAccessPointRlz: Could not clear the RLZ value.");
+ return false;
+ }
+ return true;
+}
+
+bool RlzValueStoreRegistry::AddProductEvent(Product product,
+ const char* event_rlz) {
+ std::wstring event_rlz_wide(ASCIIToWide(event_rlz));
+ base::win::RegKey reg_key;
+ GetEventsRegKey(kEventsSubkeyName, &product, KEY_WRITE, &reg_key);
+ if (reg_key.WriteValue(event_rlz_wide.c_str(), 1) != ERROR_SUCCESS) {
+ ASSERT_STRING("AddProductEvent: Could not write the new event value");
+ return false;
+ }
+
+ return true;
+}
+
+bool RlzValueStoreRegistry::ReadProductEvents(Product product,
+ std::vector<std::string>* events) {
+ // Open the events key.
+ base::win::RegKey events_key;
+ GetEventsRegKey(kEventsSubkeyName, &product, KEY_READ, &events_key);
+ if (!events_key.Valid())
+ return false;
+
+ // Append the events to the buffer.
+ int num_values = 0;
+ LONG result = ERROR_SUCCESS;
+ for (num_values = 0; result == ERROR_SUCCESS; ++num_values) {
+ // Max 32767 bytes according to MSDN, but we never use that much.
+ const size_t kMaxValueNameLength = 2048;
+ char buffer[kMaxValueNameLength];
+ DWORD size = arraysize(buffer);
+
+ result = RegEnumValueA(events_key.Handle(), num_values, buffer, &size,
+ NULL, NULL, NULL, NULL);
+ if (result == ERROR_SUCCESS)
+ events->push_back(std::string(buffer));
+ }
+
+ return result == ERROR_NO_MORE_ITEMS;
+}
+
+bool RlzValueStoreRegistry::ClearProductEvent(Product product,
+ const char* event_rlz) {
+ std::wstring event_rlz_wide(ASCIIToWide(event_rlz));
+ base::win::RegKey key;
+ GetEventsRegKey(kEventsSubkeyName, &product, KEY_WRITE, &key);
+ key.DeleteValue(event_rlz_wide.c_str());
+
+ // Verify deletion.
+ DWORD value;
+ if (key.ReadValueDW(event_rlz_wide.c_str(), &value) == ERROR_SUCCESS) {
+ ASSERT_STRING("ClearProductEvent: Could not delete the event value.");
+ return false;
+ }
+
+ return true;
+}
+
+bool RlzValueStoreRegistry::ClearAllProductEvents(Product product) {
+ return ClearAllProductEventValues(product, kEventsSubkeyName);
+}
+
+bool RlzValueStoreRegistry::AddStatefulEvent(Product product,
+ const char* event_rlz) {
+ base::win::RegKey key;
+ std::wstring event_rlz_wide(ASCIIToWide(event_rlz));
+ if (!GetEventsRegKey(kStatefulEventsSubkeyName, &product, KEY_WRITE, &key) ||
+ key.WriteValue(event_rlz_wide.c_str(), 1) != ERROR_SUCCESS) {
+ ASSERT_STRING(
+ "AddStatefulEvent: Could not write the new stateful event");
+ return false;
+ }
+
+ return true;
+}
+
+bool RlzValueStoreRegistry::IsStatefulEvent(Product product,
+ const char* event_rlz) {
+ DWORD value;
+ base::win::RegKey key;
+ GetEventsRegKey(kStatefulEventsSubkeyName, &product, KEY_READ, &key);
+ std::wstring event_rlz_wide(ASCIIToWide(event_rlz));
+ return key.ReadValueDW(event_rlz_wide.c_str(), &value) == ERROR_SUCCESS;
+}
+
+bool RlzValueStoreRegistry::ClearAllStatefulEvents(Product product) {
+ return ClearAllProductEventValues(product, kStatefulEventsSubkeyName);
+}
+
+void RlzValueStoreRegistry::CollectGarbage() {
+ // Delete each of the known subkeys if empty.
+ const char* subkeys[] = {
+ kRlzsSubkeyName,
+ kEventsSubkeyName,
+ kStatefulEventsSubkeyName,
+ kPingTimesSubkeyName
+ };
+
+ for (int i = 0; i < arraysize(subkeys); i++) {
+ std::string subkey_name;
+ base::StringAppendF(&subkey_name, "%s\\%s", kLibKeyName, subkeys[i]);
+ AppendBrandToString(&subkey_name);
+
+ VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER,
+ ASCIIToWide(subkey_name).c_str()));
+ }
+
+ // Delete the library key and its parents too now if empty.
+ VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER, GetWideLibKeyName().c_str()));
+ VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER, kGoogleCommonKeyName));
+ VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER, kGoogleKeyName));
+}
+
+ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() {
+ if (!lock_.failed())
+ store_.reset(new RlzValueStoreRegistry);
+}
+
+ScopedRlzValueStoreLock::~ScopedRlzValueStoreLock() {
+}
+
+RlzValueStore* ScopedRlzValueStoreLock::GetStore() {
+ return store_.get();
+}
+
+} // namespace rlz_lib
diff --git a/chromium/rlz/win/lib/rlz_value_store_registry.h b/chromium/rlz/win/lib/rlz_value_store_registry.h
new file mode 100644
index 00000000000..c7fae46a347
--- /dev/null
+++ b/chromium/rlz/win/lib/rlz_value_store_registry.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RLZ_WIN_LIB_RLZ_VALUE_STORE_REGISTRY_H_
+#define RLZ_WIN_LIB_RLZ_VALUE_STORE_REGISTRY_H_
+
+#include "base/compiler_specific.h"
+#include "rlz/lib/rlz_value_store.h"
+
+namespace rlz_lib {
+
+// Implements RlzValueStore by storing values in the windows registry.
+class RlzValueStoreRegistry : public RlzValueStore {
+ public:
+ static std::wstring GetWideLibKeyName();
+
+ virtual bool HasAccess(AccessType type) OVERRIDE;
+
+ virtual bool WritePingTime(Product product, int64 time) OVERRIDE;
+ virtual bool ReadPingTime(Product product, int64* time) OVERRIDE;
+ virtual bool ClearPingTime(Product product) OVERRIDE;
+
+ virtual bool WriteAccessPointRlz(AccessPoint access_point,
+ const char* new_rlz) OVERRIDE;
+ virtual bool ReadAccessPointRlz(AccessPoint access_point,
+ char* rlz,
+ size_t rlz_size) OVERRIDE;
+ virtual bool ClearAccessPointRlz(AccessPoint access_point) OVERRIDE;
+
+ virtual bool AddProductEvent(Product product, const char* event_rlz) OVERRIDE;
+ virtual bool ReadProductEvents(Product product,
+ std::vector<std::string>* events) OVERRIDE;
+ virtual bool ClearProductEvent(Product product,
+ const char* event_rlz) OVERRIDE;
+ virtual bool ClearAllProductEvents(Product product) OVERRIDE;
+
+ virtual bool AddStatefulEvent(Product product,
+ const char* event_rlz) OVERRIDE;
+ virtual bool IsStatefulEvent(Product product,
+ const char* event_rlz) OVERRIDE;
+ virtual bool ClearAllStatefulEvents(Product product) OVERRIDE;
+
+ virtual void CollectGarbage() OVERRIDE;
+
+ private:
+ RlzValueStoreRegistry() {}
+ DISALLOW_COPY_AND_ASSIGN(RlzValueStoreRegistry);
+ friend class ScopedRlzValueStoreLock;
+};
+
+} // namespace rlz_lib
+
+#endif // RLZ_WIN_LIB_RLZ_VALUE_STORE_REGISTRY_H_
+