├── COPYING ├── DEPS ├── README.md ├── lib ├── assert.cc ├── assert.h ├── crc32.h ├── crc32_unittest.cc ├── crc32_wrapper.cc ├── crc8.cc ├── crc8.h ├── crc8_unittest.cc ├── financial_ping.cc ├── financial_ping.h ├── financial_ping_test.cc ├── lib_values.cc ├── lib_values.h ├── lib_values_unittest.cc ├── machine_id.cc ├── machine_id.h ├── machine_id_unittest.cc ├── rlz_enums.h ├── rlz_lib.cc ├── rlz_lib.h ├── rlz_lib_clear.cc ├── rlz_lib_test.cc ├── rlz_value_store.h ├── string_utils.cc ├── string_utils.h └── string_utils_unittest.cc ├── mac └── lib │ ├── machine_id_mac.cc │ ├── rlz_value_store_mac.h │ └── rlz_value_store_mac.mm ├── rlz.gyp ├── test ├── rlz_test_helpers.cc ├── rlz_test_helpers.h └── rlz_unittest_main.cc └── win ├── dll ├── dll_main.cc └── exports.cc └── lib ├── lib_mutex.cc ├── lib_mutex.h ├── machine_deal.cc ├── machine_deal.h ├── machine_deal_test.cc ├── machine_id_win.cc ├── process_info.cc ├── process_info.h ├── registry_util.cc ├── registry_util.h ├── rlz_lib.h ├── rlz_lib_win.cc ├── rlz_value_store_registry.cc ├── rlz_value_store_registry.h └── vista_winnt.h /COPYING: -------------------------------------------------------------------------------- 1 | Copyright 2010 Google Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /DEPS: -------------------------------------------------------------------------------- 1 | # Copyright 2010 Google Inc. All Rights Reserved. 2 | # Use of this source code is governed by an Apache-style license that can be 3 | # found in the COPYING file. 4 | 5 | vars = { 6 | "chrev": "@119173" 7 | } 8 | 9 | deps = { 10 | "src/base": 11 | "http://src.chromium.org/svn/trunk/src/base" + Var("chrev"), 12 | 13 | "src/build": 14 | "http://src.chromium.org/svn/trunk/src/build" + Var("chrev"), 15 | 16 | "src/third_party/icu": 17 | "http://src.chromium.org/svn/trunk/deps/third_party/icu42" + Var("chrev"), 18 | 19 | "src/third_party/modp_b64": 20 | "http://src.chromium.org/svn/trunk/src/third_party/modp_b64" + Var("chrev"), 21 | 22 | "src/third_party/nss": 23 | "http://src.chromium.org/svn/trunk/deps/third_party/nss" + Var("chrev"), 24 | 25 | "src/third_party/sqlite": 26 | "http://src.chromium.org/svn/trunk/src/third_party/sqlite" + Var("chrev"), 27 | 28 | "src/third_party/wtl": 29 | "http://src.chromium.org/svn/trunk/src/third_party/wtl" + Var("chrev"), 30 | 31 | "src/third_party/zlib": 32 | "http://src.chromium.org/svn/trunk/src/third_party/zlib" + Var("chrev"), 33 | 34 | "src/testing": 35 | "http://src.chromium.org/svn/trunk/src/testing" + Var("chrev"), 36 | 37 | "src/testing/gmock": 38 | "http://googlemock.googlecode.com/svn/trunk@374", 39 | 40 | "src/testing/gtest": 41 | "http://googletest.googlecode.com/svn/trunk@492", 42 | 43 | "src/tools/gyp": 44 | "http://gyp.googlecode.com/svn/trunk@1233", 45 | 46 | "src/tools/win": 47 | "http://src.chromium.org/svn/trunk/src/tools/win" + Var("chrev"), 48 | 49 | # If using rlz with chrome's networking library, add it and its dependencies 50 | # here. 51 | } 52 | 53 | include_rules = [ 54 | "+build", 55 | "+net", # This is only used when force_rlz_use_chrome_net=1 is passed to gyp. 56 | "+third_party/zlib", 57 | ] 58 | 59 | hooks = [ 60 | { 61 | # A change to a .gyp, .gypi, or to GYP itself should run the generator. 62 | "pattern": ".", 63 | "action": ["python", "src/build/gyp_chromium", "src/rlz/rlz.gyp"], 64 | } 65 | ] 66 | 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **NOTE: As of 6/21/2012, the source code has moved to the Chromium repository. The instructions in the wiki have been updated.** 2 | 3 | The RLZ project is a library for grouping promotion event signals and anonymous user cohorts. 4 | 5 | ![http://rlz.googlecode.com/files/rlz_exploded.png](http://rlz.googlecode.com/files/rlz_exploded.png) 6 | 7 | Client applications with the RLZ library can use explicit cohort tagging to manage promotion analysis. A client application with a particular tag can transmit that tag as it chooses for payments and analysis purposes. As an example, the RLZ parameter "rlz=1T4AAAA\_enUS202" indicates the client application is Toolbar version 4, distributed with AAAA software bundle, English version, to a US user in December 2006. This empowers computation of metrics broken down into useful dimensions. 8 | 9 | Find out more about [how to read RLZ strings](https://github.com/rogerta/rlz/blob/wiki/HowToReadAnRlzString.md), how the code [works](https://github.com/rogerta/rlz/tree/master/lib) in general, and how it is used in the [Google Chrome](http://www.google.com/intl/en/landing/chrome/google-chrome-privacy-whitepaper.pdf) web browser. 10 | -------------------------------------------------------------------------------- /lib/assert.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // Macros specific to the RLZ library. 6 | 7 | #include "rlz/lib/assert.h" 8 | 9 | namespace rlz_lib { 10 | 11 | #ifdef MUTE_EXPECTED_ASSERTS 12 | std::string expected_assertion_; 13 | #endif 14 | 15 | } // namespace rlz_lib 16 | -------------------------------------------------------------------------------- /lib/assert.h: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // Macros specific to the RLZ library. 6 | 7 | #ifndef RLZ_LIB_ASSERT_H_ 8 | #define RLZ_LIB_ASSERT_H_ 9 | 10 | #include 11 | #include "base/logging.h" 12 | 13 | // An assertion macro. 14 | // Can mute expected assertions in debug mode. 15 | 16 | #ifndef ASSERT_STRING 17 | #ifndef MUTE_EXPECTED_ASSERTS 18 | #define ASSERT_STRING(expr) LOG_IF(FATAL, false) << (expr) 19 | #else 20 | #define ASSERT_STRING(expr) \ 21 | do { \ 22 | std::string expr_string(expr); \ 23 | if (rlz_lib::expected_assertion_ != expr_string) { \ 24 | LOG_IF(FATAL, false) << (expr); \ 25 | } \ 26 | } while (0) 27 | #endif 28 | #endif 29 | 30 | 31 | #ifndef VERIFY 32 | #ifdef _DEBUG 33 | #define VERIFY(expr) LOG_IF(FATAL, !(expr)) << #expr 34 | #else 35 | #define VERIFY(expr) (void)(expr) 36 | #endif 37 | #endif 38 | 39 | namespace rlz_lib { 40 | 41 | #ifdef MUTE_EXPECTED_ASSERTS 42 | extern std::string expected_assertion_; 43 | #endif 44 | 45 | inline void SetExpectedAssertion(const char* s) { 46 | #ifdef MUTE_EXPECTED_ASSERTS 47 | expected_assertion_ = s; 48 | #endif 49 | } 50 | 51 | } // rlz_lib 52 | 53 | #endif // RLZ_LIB_ASSERT_H_ 54 | -------------------------------------------------------------------------------- /lib/crc32.h: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // A wrapper around ZLib's CRC function. 6 | 7 | #ifndef RLZ_LIB_CRC32_H_ 8 | #define RLZ_LIB_CRC32_H_ 9 | 10 | namespace rlz_lib { 11 | 12 | int Crc32(const unsigned char* buf, int length); 13 | bool Crc32(const char* text, int* crc); 14 | 15 | } // namespace rlz_lib 16 | 17 | #endif // RLZ_LIB_CRC32_H_ 18 | -------------------------------------------------------------------------------- /lib/crc32_unittest.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // A test for ZLib's checksum function. 6 | 7 | #include "rlz/lib/crc32.h" 8 | 9 | #include "base/logging.h" 10 | #include "testing/gmock/include/gmock/gmock.h" 11 | #include "testing/gtest/include/gtest/gtest.h" 12 | 13 | TEST(Crc32Unittest, ByteTest) { 14 | struct { 15 | const char* data; 16 | int len; 17 | // Externally calculated at http://crc32-checksum.waraxe.us/ 18 | int crc; 19 | } kData[] = { 20 | {"Hello" , 5, 0xF7D18982}, 21 | {"Google" , 6, 0x62B0F067}, 22 | {"" , 0, 0x0}, 23 | {"One more string.", 16, 0x0CA14970}, 24 | {NULL , 0, 0x0}, 25 | }; 26 | 27 | for (int i = 0; kData[i].data; i++) 28 | EXPECT_EQ(kData[i].crc, 29 | rlz_lib::Crc32(reinterpret_cast(kData[i].data), 30 | kData[i].len)); 31 | } 32 | 33 | TEST(Crc32Unittest, CharTest) { 34 | struct { 35 | const char* data; 36 | // Externally calculated at http://crc32-checksum.waraxe.us/ 37 | int crc; 38 | } kData[] = { 39 | {"Hello" , 0xF7D18982}, 40 | {"Google" , 0x62B0F067}, 41 | {"" , 0x0}, 42 | {"One more string.", 0x0CA14970}, 43 | {"Google\r\n" , 0x83A3E860}, 44 | {NULL , 0x0}, 45 | }; 46 | 47 | int crc; 48 | for (int i = 0; kData[i].data; i++) { 49 | EXPECT_TRUE(rlz_lib::Crc32(kData[i].data, &crc)); 50 | EXPECT_EQ(kData[i].crc, crc); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/crc32_wrapper.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // A wrapper around ZLib's CRC functions to put them in the rlz_lib namespace 6 | // and use our types. 7 | 8 | #include "rlz/lib/assert.h" 9 | #include "rlz/lib/crc32.h" 10 | #include "rlz/lib/string_utils.h" 11 | #include "third_party/zlib/zlib.h" 12 | 13 | namespace rlz_lib { 14 | 15 | int Crc32(const unsigned char* buf, int length) { 16 | return crc32(0L, buf, length); 17 | } 18 | 19 | bool Crc32(const char* text, int* crc) { 20 | if (!crc) { 21 | ASSERT_STRING("Crc32: crc is NULL."); 22 | return false; 23 | } 24 | 25 | *crc = 0; 26 | for (int i = 0; text[i]; i++) { 27 | if (!IsAscii(text[i])) 28 | return false; 29 | 30 | *crc = crc32(*crc, reinterpret_cast(text + i), 1); 31 | } 32 | 33 | return true; 34 | } 35 | 36 | } // namespace rlz_lib 37 | -------------------------------------------------------------------------------- /lib/crc8.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | 5 | #include "rlz/lib/crc8.h" 6 | 7 | namespace { 8 | 9 | // The CRC lookup table used for ATM HES (Polynomial = 0x07). 10 | // These are 256 unique 8-bit values. 11 | const unsigned char kCrcTable[256] = { 12 | 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 13 | 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, 14 | 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 15 | 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, 16 | 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 17 | 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, 18 | 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 19 | 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, 20 | 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 21 | 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, 22 | 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 23 | 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, 24 | 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 25 | 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, 26 | 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 27 | 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, 28 | 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 29 | 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, 30 | 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 31 | 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, 32 | 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 33 | 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, 34 | 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 35 | 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, 36 | 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 37 | 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, 38 | 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 39 | 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, 40 | 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 41 | 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, 42 | 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 43 | 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 44 | }; 45 | 46 | } // namespace anonymous 47 | 48 | 49 | namespace rlz_lib { 50 | 51 | bool Crc8::Generate(const unsigned char *data, int length, 52 | unsigned char* check_sum) { 53 | if (!check_sum) 54 | return false; 55 | 56 | *check_sum = 0; 57 | if (!data) 58 | return false; 59 | 60 | // The inital and final constants are as used in the ATM HEC. 61 | static const unsigned char kInitial = 0x00; 62 | static const unsigned char kFinal = 0x55; 63 | unsigned char crc = kInitial; 64 | for (int i = 0; i < length; ++i) { 65 | crc = kCrcTable[(data[i] ^ crc) & 0xFFU]; 66 | } 67 | 68 | *check_sum = crc ^ kFinal; 69 | return true; 70 | } 71 | 72 | bool Crc8::Verify(const unsigned char* data, int length, 73 | unsigned char check_sum, bool* matches) { 74 | if (!matches) 75 | return false; 76 | 77 | *matches = false; 78 | if (!data) 79 | return false; 80 | 81 | unsigned char calculated_crc; 82 | if (!Generate(data, length, &calculated_crc)) 83 | return false; 84 | 85 | *matches = check_sum == calculated_crc; 86 | 87 | return true; 88 | } 89 | 90 | } // namespace rlz_lib 91 | -------------------------------------------------------------------------------- /lib/crc8.h: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // Crc8 utility functions. 6 | 7 | #ifndef RLZ_LIB_CRC8_H_ 8 | #define RLZ_LIB_CRC8_H_ 9 | 10 | namespace rlz_lib { 11 | // CRC-8 methods: 12 | class Crc8 { 13 | public: 14 | static bool Generate(const unsigned char* data, 15 | int length, 16 | unsigned char* check_sum); 17 | static bool Verify(const unsigned char* data, 18 | int length, 19 | unsigned char checksum, 20 | bool * matches); 21 | }; 22 | }; // namespace rlz_lib 23 | 24 | #endif // RLZ_LIB_CRC8_H_ 25 | -------------------------------------------------------------------------------- /lib/crc8_unittest.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // Uniitest for data encryption functions. 6 | 7 | #include "base/logging.h" 8 | #include "testing/gmock/include/gmock/gmock.h" 9 | #include "testing/gtest/include/gtest/gtest.h" 10 | 11 | #include "rlz/lib/crc8.h" 12 | 13 | TEST(Crc8Unittest, TestCrc8) { 14 | struct Data { 15 | char string[10]; 16 | // Externally calculated checksums use 17 | // http://www.zorc.breitbandkatze.de/crc.html 18 | // with the ATM HEC paramters: 19 | // CRC-8, Polynomial 0x07, Initial value 0x00, Final XOR value 0x55 20 | // (direct, don't reverse data byes, don't reverse CRC before final XOR) 21 | unsigned char external_crc; 22 | int random_byte; 23 | unsigned char corrupt_value; 24 | } data[] = { 25 | {"Google", 0x01, 2, 0x53}, 26 | {"GOOGLE", 0xA6, 4, 0x11}, 27 | {"My CRC 8!", 0xDC, 0, 0x50}, 28 | }; 29 | 30 | unsigned char* bytes; 31 | unsigned char crc; 32 | bool matches; 33 | int length; 34 | for (size_t i = 0; i < sizeof(data) / sizeof(data[0]); ++i) { 35 | bytes = reinterpret_cast(data[i].string); 36 | crc = 0; 37 | matches = false; 38 | length = strlen(data[i].string); 39 | 40 | // Calculate CRC and compare against external value. 41 | rlz_lib::Crc8::Generate(bytes, length, &crc); 42 | EXPECT_TRUE(crc == data[i].external_crc); 43 | rlz_lib::Crc8::Verify(bytes, length, crc, &matches); 44 | EXPECT_TRUE(matches); 45 | 46 | // Corrupt string and see if CRC still matches. 47 | data[i].string[data[i].random_byte] = data[i].corrupt_value; 48 | rlz_lib::Crc8::Verify(bytes, length, crc, &matches); 49 | EXPECT_FALSE(matches); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/financial_ping.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // Library functions related to the Financial Server ping. 6 | 7 | #include "rlz/lib/financial_ping.h" 8 | 9 | #include "base/basictypes.h" 10 | #include "base/memory/scoped_ptr.h" 11 | #include "base/string_util.h" 12 | #include "base/stringprintf.h" 13 | #include "base/utf_string_conversions.h" 14 | #include "rlz/lib/assert.h" 15 | #include "rlz/lib/lib_values.h" 16 | #include "rlz/lib/machine_id.h" 17 | #include "rlz/lib/rlz_lib.h" 18 | #include "rlz/lib/rlz_value_store.h" 19 | #include "rlz/lib/string_utils.h" 20 | 21 | #if !defined(OS_WIN) 22 | #include "base/time.h" 23 | #endif 24 | 25 | #if defined(RLZ_NETWORK_IMPLEMENTATION_WIN_INET) 26 | 27 | #include 28 | #include 29 | 30 | namespace { 31 | 32 | class InternetHandle { 33 | public: 34 | InternetHandle(HINTERNET handle) { handle_ = handle; } 35 | ~InternetHandle() { if (handle_) InternetCloseHandle(handle_); } 36 | operator HINTERNET() const { return handle_; } 37 | bool operator!() const { return (handle_ == NULL); } 38 | 39 | private: 40 | HINTERNET handle_; 41 | }; 42 | 43 | } // namespace 44 | 45 | #else 46 | 47 | #include "base/bind.h" 48 | #include "base/message_loop.h" 49 | #include "base/time.h" 50 | #include "googleurl/src/gurl.h" 51 | #include "net/base/load_flags.h" 52 | #include "net/url_request/url_fetcher.h" 53 | #include "net/url_request/url_fetcher_delegate.h" 54 | #include "net/url_request/url_request_context.h" 55 | #include "net/url_request/url_request_context_getter.h" 56 | 57 | #endif 58 | 59 | namespace { 60 | 61 | // Returns the time relative to a fixed point in the past in multiples of 62 | // 100 ns stepts. The point in the past is arbitrary but can't change, as the 63 | // result of this value is stored on disk. 64 | int64 GetSystemTimeAsInt64() { 65 | #if defined(OS_WIN) 66 | FILETIME now_as_file_time; 67 | // Relative to Jan 1, 1601 (UTC). 68 | GetSystemTimeAsFileTime(&now_as_file_time); 69 | 70 | LARGE_INTEGER integer; 71 | integer.HighPart = now_as_file_time.dwHighDateTime; 72 | integer.LowPart = now_as_file_time.dwLowDateTime; 73 | return integer.QuadPart; 74 | #else 75 | // Seconds since epoch (Jan 1, 1970). 76 | double now_seconds = base::Time::Now().ToDoubleT(); 77 | return static_cast(now_seconds * 1000 * 1000 * 10); 78 | #endif 79 | } 80 | 81 | } // namespace 82 | 83 | 84 | namespace rlz_lib { 85 | 86 | bool FinancialPing::FormRequest(Product product, 87 | const AccessPoint* access_points, const char* product_signature, 88 | const char* product_brand, const char* product_id, 89 | const char* product_lang, bool exclude_machine_id, 90 | std::string* request) { 91 | if (!request) { 92 | ASSERT_STRING("FinancialPing::FormRequest: request is NULL"); 93 | return false; 94 | } 95 | 96 | request->clear(); 97 | 98 | ScopedRlzValueStoreLock lock; 99 | RlzValueStore* store = lock.GetStore(); 100 | if (!store || !store->HasAccess(RlzValueStore::kReadAccess)) 101 | return false; 102 | 103 | if (!access_points) { 104 | ASSERT_STRING("FinancialPing::FormRequest: access_points is NULL"); 105 | return false; 106 | } 107 | 108 | if (!product_signature) { 109 | ASSERT_STRING("FinancialPing::FormRequest: product_signature is NULL"); 110 | return false; 111 | } 112 | 113 | if (!SupplementaryBranding::GetBrand().empty()) { 114 | if (SupplementaryBranding::GetBrand() != product_brand) { 115 | ASSERT_STRING("FinancialPing::FormRequest: supplementary branding bad"); 116 | return false; 117 | } 118 | } 119 | 120 | base::StringAppendF(request, "%s?", kFinancialPingPath); 121 | 122 | // Add the signature, brand, product id and language. 123 | base::StringAppendF(request, "%s=%s", kProductSignatureCgiVariable, 124 | product_signature); 125 | if (product_brand) 126 | base::StringAppendF(request, "&%s=%s", kProductBrandCgiVariable, 127 | product_brand); 128 | 129 | if (product_id) 130 | base::StringAppendF(request, "&%s=%s", kProductIdCgiVariable, product_id); 131 | 132 | if (product_lang) 133 | base::StringAppendF(request, "&%s=%s", kProductLanguageCgiVariable, 134 | product_lang); 135 | 136 | // Add the product events. 137 | char cgi[kMaxCgiLength + 1]; 138 | cgi[0] = 0; 139 | bool has_events = GetProductEventsAsCgi(product, cgi, arraysize(cgi)); 140 | if (has_events) 141 | base::StringAppendF(request, "&%s", cgi); 142 | 143 | // If we don't have any events, we should ping all the AP's on the system 144 | // that we know about and have a current RLZ value, even if they are not 145 | // used by this product. 146 | AccessPoint all_points[LAST_ACCESS_POINT]; 147 | if (!has_events) { 148 | char rlz[kMaxRlzLength + 1]; 149 | int idx = 0; 150 | for (int ap = NO_ACCESS_POINT + 1; ap < LAST_ACCESS_POINT; ap++) { 151 | rlz[0] = 0; 152 | AccessPoint point = static_cast(ap); 153 | if (GetAccessPointRlz(point, rlz, arraysize(rlz)) && 154 | rlz[0] != '\0') 155 | all_points[idx++] = point; 156 | } 157 | all_points[idx] = NO_ACCESS_POINT; 158 | } 159 | 160 | // Add the RLZ's and the DCC if needed. This is the same as get PingParams. 161 | // This will also include the RLZ Exchange Protocol CGI Argument. 162 | cgi[0] = 0; 163 | if (GetPingParams(product, has_events ? access_points : all_points, 164 | cgi, arraysize(cgi))) 165 | base::StringAppendF(request, "&%s", cgi); 166 | 167 | if (has_events && !exclude_machine_id) { 168 | std::string machine_id; 169 | if (GetMachineId(&machine_id)) { 170 | base::StringAppendF(request, "&%s=%s", kMachineIdCgiVariable, 171 | machine_id.c_str()); 172 | } 173 | } 174 | 175 | return true; 176 | } 177 | 178 | #if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET) 179 | // The URLRequestContextGetter used by FinancialPing::PingServer(). 180 | net::URLRequestContextGetter* g_context; 181 | 182 | bool FinancialPing::SetURLRequestContext( 183 | net::URLRequestContextGetter* context) { 184 | ScopedRlzValueStoreLock lock; 185 | RlzValueStore* store = lock.GetStore(); 186 | if (!store) 187 | return false; 188 | 189 | g_context = context; 190 | return true; 191 | } 192 | 193 | namespace { 194 | 195 | class FinancialPingUrlFetcherDelegate : public net::URLFetcherDelegate { 196 | public: 197 | FinancialPingUrlFetcherDelegate(MessageLoop* loop) : loop_(loop) { } 198 | virtual void OnURLFetchComplete(const net::URLFetcher* source); 199 | private: 200 | MessageLoop* loop_; 201 | }; 202 | 203 | void FinancialPingUrlFetcherDelegate::OnURLFetchComplete( 204 | const net::URLFetcher* source) { 205 | loop_->Quit(); 206 | } 207 | 208 | } // namespace 209 | 210 | #endif 211 | 212 | bool FinancialPing::PingServer(const char* request, std::string* response) { 213 | if (!response) 214 | return false; 215 | 216 | response->clear(); 217 | 218 | #if defined(RLZ_NETWORK_IMPLEMENTATION_WIN_INET) 219 | // Initialize WinInet. 220 | InternetHandle inet_handle = InternetOpenA(kFinancialPingUserAgent, 221 | INTERNET_OPEN_TYPE_PRECONFIG, 222 | NULL, NULL, 0); 223 | if (!inet_handle) 224 | return false; 225 | 226 | // Open network connection. 227 | InternetHandle connection_handle = InternetConnectA(inet_handle, 228 | kFinancialServer, kFinancialPort, "", "", INTERNET_SERVICE_HTTP, 229 | INTERNET_FLAG_NO_CACHE_WRITE, 0); 230 | if (!connection_handle) 231 | return false; 232 | 233 | // Prepare the HTTP request. 234 | InternetHandle http_handle = HttpOpenRequestA(connection_handle, 235 | "GET", request, NULL, NULL, kFinancialPingResponseObjects, 236 | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES, NULL); 237 | if (!http_handle) 238 | return false; 239 | 240 | // Timeouts are probably: 241 | // INTERNET_OPTION_SEND_TIMEOUT, INTERNET_OPTION_RECEIVE_TIMEOUT 242 | 243 | // Send the HTTP request. Note: Fails if user is working in off-line mode. 244 | if (!HttpSendRequest(http_handle, NULL, 0, NULL, 0)) 245 | return false; 246 | 247 | // Check the response status. 248 | DWORD status; 249 | DWORD status_size = sizeof(status); 250 | if (!HttpQueryInfo(http_handle, HTTP_QUERY_STATUS_CODE | 251 | HTTP_QUERY_FLAG_NUMBER, &status, &status_size, NULL) || 252 | 200 != status) 253 | return false; 254 | 255 | // Get the response text. 256 | scoped_array buffer(new char[kMaxPingResponseLength]); 257 | if (buffer.get() == NULL) 258 | return false; 259 | 260 | DWORD bytes_read = 0; 261 | while (InternetReadFile(http_handle, buffer.get(), kMaxPingResponseLength, 262 | &bytes_read) && bytes_read > 0) { 263 | response->append(buffer.get(), bytes_read); 264 | bytes_read = 0; 265 | }; 266 | 267 | return true; 268 | #else 269 | // Run a blocking event loop to match the win inet implementation. 270 | MessageLoop loop; 271 | FinancialPingUrlFetcherDelegate delegate(&loop); 272 | 273 | std::string url = base::StringPrintf("http://%s:%d%s", 274 | kFinancialServer, kFinancialPort, 275 | request); 276 | 277 | scoped_ptr fetcher(net::URLFetcher::Create( 278 | GURL(url), net::URLFetcher::GET, &delegate)); 279 | 280 | fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE | 281 | net::LOAD_DO_NOT_SEND_AUTH_DATA | 282 | net::LOAD_DO_NOT_PROMPT_FOR_LOGIN | 283 | net::LOAD_DO_NOT_SEND_COOKIES | 284 | net::LOAD_DO_NOT_SAVE_COOKIES); 285 | 286 | // Ensure rlz_lib::SetURLRequestContext() has been called before sending 287 | // pings. 288 | CHECK(g_context); 289 | fetcher->SetRequestContext(g_context); 290 | 291 | const base::TimeDelta kTimeout = base::TimeDelta::FromMinutes(5); 292 | loop.PostTask( 293 | FROM_HERE, 294 | base::Bind(&net::URLFetcher::Start, base::Unretained(fetcher.get()))); 295 | loop.PostNonNestableDelayedTask( 296 | FROM_HERE, MessageLoop::QuitClosure(), kTimeout); 297 | 298 | loop.Run(); 299 | 300 | if (fetcher->GetResponseCode() != 200) 301 | return false; 302 | 303 | return fetcher->GetResponseAsString(response); 304 | #endif 305 | } 306 | 307 | bool FinancialPing::IsPingTime(Product product, bool no_delay) { 308 | ScopedRlzValueStoreLock lock; 309 | RlzValueStore* store = lock.GetStore(); 310 | if (!store || !store->HasAccess(RlzValueStore::kReadAccess)) 311 | return false; 312 | 313 | int64 last_ping = 0; 314 | if (!store->ReadPingTime(product, &last_ping)) 315 | return true; 316 | 317 | uint64 now = GetSystemTimeAsInt64(); 318 | int64 interval = now - last_ping; 319 | 320 | // If interval is negative, clock was probably reset. So ping. 321 | if (interval < 0) 322 | return true; 323 | 324 | // Check if this product has any unreported events. 325 | char cgi[kMaxCgiLength + 1]; 326 | cgi[0] = 0; 327 | bool has_events = GetProductEventsAsCgi(product, cgi, arraysize(cgi)); 328 | if (no_delay && has_events) 329 | return true; 330 | 331 | return interval >= (has_events ? kEventsPingInterval : kNoEventsPingInterval); 332 | } 333 | 334 | 335 | bool FinancialPing::UpdateLastPingTime(Product product) { 336 | ScopedRlzValueStoreLock lock; 337 | RlzValueStore* store = lock.GetStore(); 338 | if (!store || !store->HasAccess(RlzValueStore::kWriteAccess)) 339 | return false; 340 | 341 | uint64 now = GetSystemTimeAsInt64(); 342 | return store->WritePingTime(product, now); 343 | } 344 | 345 | 346 | bool FinancialPing::ClearLastPingTime(Product product) { 347 | ScopedRlzValueStoreLock lock; 348 | RlzValueStore* store = lock.GetStore(); 349 | if (!store || !store->HasAccess(RlzValueStore::kWriteAccess)) 350 | return false; 351 | return store->ClearPingTime(product); 352 | } 353 | 354 | } // namespace 355 | -------------------------------------------------------------------------------- /lib/financial_ping.h: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // Library functions related to the Financial Server ping. 6 | 7 | #ifndef RLZ_LIB_FINANCIAL_PING_H_ 8 | #define RLZ_LIB_FINANCIAL_PING_H_ 9 | 10 | #include 11 | #include "rlz/lib/rlz_enums.h" 12 | 13 | #if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET) 14 | namespace net { 15 | class URLRequestContextGetter; 16 | } // namespace net 17 | #endif 18 | 19 | namespace rlz_lib { 20 | 21 | class FinancialPing { 22 | public: 23 | // Form the HTTP request to send to the PSO server. 24 | // Will look something like: 25 | // /pso/ping?as=swg&brand=GGLD&id=124&hl=en& 26 | // events=I7S&rep=1&rlz=I7:val,W1:&dcc=dval 27 | static bool FormRequest(Product product, const AccessPoint* access_points, 28 | const char* product_signature, 29 | const char* product_brand, const char* product_id, 30 | const char* product_lang, bool exclude_machine_id, 31 | std::string* request); 32 | 33 | // Returns whether the time is right to send a ping. 34 | // If no_delay is true, this should always ping if there are events, 35 | // or one week has passed since last_ping when there are no new events. 36 | // If no_delay is false, this should ping if current time < last_ping time 37 | // (case of time reset) or if one day has passed since last_ping and there 38 | // are events, or one week has passed since last_ping when there are 39 | // no new events. 40 | static bool IsPingTime(Product product, bool no_delay); 41 | 42 | // Set the last ping time to be now. Writes to RlzValueStore. 43 | static bool UpdateLastPingTime(Product product); 44 | 45 | // Clear the last ping time - should be called on uninstall. 46 | // Writes to RlzValueStore. 47 | static bool ClearLastPingTime(Product product); 48 | 49 | // Ping the financial server with request. Writes to RlzValueStore. 50 | static bool PingServer(const char* request, std::string* response); 51 | 52 | #if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET) 53 | static bool SetURLRequestContext(net::URLRequestContextGetter* context); 54 | #endif 55 | 56 | private: 57 | FinancialPing() {} 58 | ~FinancialPing() {} 59 | }; 60 | 61 | } // namespace rlz_lib 62 | 63 | 64 | #endif // RLZ_LIB_FINANCIAL_PING_H_ 65 | -------------------------------------------------------------------------------- /lib/financial_ping_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // A test application for the FinancialPing class. 6 | // 7 | // These tests should not be executed on the build server: 8 | // - They modify machine state (registry). 9 | // 10 | // These tests require write access to HKCU and HKLM. 11 | // 12 | // The "GGLA" brand is used to test the normal code flow of the code, and the 13 | // "TEST" brand is used to test the supplementary brand code code flow. In one 14 | // case below, the brand "GOOG" is used because the code wants to use a brand 15 | // that is neither of the two mentioned above. 16 | 17 | #include "rlz/lib/financial_ping.h" 18 | 19 | #include "base/basictypes.h" 20 | #include "base/logging.h" 21 | #include "base/string_util.h" 22 | #include "base/stringprintf.h" 23 | #include "base/utf_string_conversions.h" 24 | #include "rlz/lib/lib_values.h" 25 | #include "rlz/lib/machine_id.h" 26 | #include "rlz/lib/rlz_lib.h" 27 | #include "rlz/lib/rlz_value_store.h" 28 | #include "rlz/test/rlz_test_helpers.h" 29 | #include "testing/gmock/include/gmock/gmock.h" 30 | #include "testing/gtest/include/gtest/gtest.h" 31 | 32 | #if defined(OS_WIN) 33 | #include "rlz/win/lib/machine_deal.h" 34 | #else 35 | #include "base/time.h" 36 | #endif 37 | 38 | namespace { 39 | 40 | // Must match the implementation in file_time.cc. 41 | int64 GetSystemTimeAsInt64() { 42 | #if defined(OS_WIN) 43 | FILETIME now_as_file_time; 44 | GetSystemTimeAsFileTime(&now_as_file_time); 45 | LARGE_INTEGER integer; 46 | integer.HighPart = now_as_file_time.dwHighDateTime; 47 | integer.LowPart = now_as_file_time.dwLowDateTime; 48 | return integer.QuadPart; 49 | #else 50 | double now_seconds = base::Time::Now().ToDoubleT(); 51 | return static_cast(now_seconds * 1000 * 1000 * 10); 52 | #endif 53 | } 54 | 55 | // Ping times in 100-nanosecond intervals. 56 | const int64 k1MinuteInterval = 60LL * 10000000LL; // 1 minute 57 | 58 | } // namespace anonymous 59 | 60 | class FinancialPingTest : public RlzLibTestBase { 61 | }; 62 | 63 | TEST_F(FinancialPingTest, FormRequest) { 64 | std::string brand_string = rlz_lib::SupplementaryBranding::GetBrand(); 65 | const char* brand = brand_string.empty() ? "GGLA" : brand_string.c_str(); 66 | 67 | #if defined(OS_WIN) 68 | EXPECT_TRUE(rlz_lib::MachineDealCode::Set("dcc_value")); 69 | #define DCC_PARAM "&dcc=dcc_value" 70 | #else 71 | #define DCC_PARAM "" 72 | #endif 73 | 74 | EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, 75 | "TbRlzValue")); 76 | 77 | EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER)); 78 | EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER, 79 | rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE)); 80 | EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER, 81 | rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL)); 82 | 83 | rlz_lib::AccessPoint points[] = 84 | {rlz_lib::IETB_SEARCH_BOX, rlz_lib::NO_ACCESS_POINT, 85 | rlz_lib::NO_ACCESS_POINT}; 86 | 87 | std::string machine_id; 88 | bool got_machine_id = rlz_lib::GetMachineId(&machine_id); 89 | 90 | std::string request; 91 | EXPECT_TRUE(rlz_lib::FinancialPing::FormRequest(rlz_lib::TOOLBAR_NOTIFIER, 92 | points, "swg", brand, NULL, "en", false, &request)); 93 | std::string expected_response; 94 | base::StringAppendF(&expected_response, 95 | "/tools/pso/ping?as=swg&brand=%s&hl=en&" 96 | "events=I7S,W1I&rep=2&rlz=T4:TbRlzValue" DCC_PARAM 97 | , brand); 98 | 99 | if (got_machine_id) 100 | base::StringAppendF(&expected_response, "&id=%s", machine_id.c_str()); 101 | EXPECT_EQ(expected_response, request); 102 | 103 | EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, "")); 104 | EXPECT_TRUE(rlz_lib::FinancialPing::FormRequest(rlz_lib::TOOLBAR_NOTIFIER, 105 | points, "swg", brand, "IdOk2", NULL, false, &request)); 106 | expected_response.clear(); 107 | base::StringAppendF(&expected_response, 108 | "/tools/pso/ping?as=swg&brand=%s&pid=IdOk2&" 109 | "events=I7S,W1I&rep=2&rlz=T4:" DCC_PARAM, brand); 110 | 111 | if (got_machine_id) 112 | base::StringAppendF(&expected_response, "&id=%s", machine_id.c_str()); 113 | EXPECT_EQ(expected_response, request); 114 | 115 | EXPECT_TRUE(rlz_lib::FinancialPing::FormRequest(rlz_lib::TOOLBAR_NOTIFIER, 116 | points, "swg", brand, "IdOk", NULL, true, &request)); 117 | expected_response.clear(); 118 | base::StringAppendF(&expected_response, 119 | "/tools/pso/ping?as=swg&brand=%s&pid=IdOk&" 120 | "events=I7S,W1I&rep=2&rlz=T4:" DCC_PARAM, brand); 121 | EXPECT_EQ(expected_response, request); 122 | 123 | EXPECT_TRUE(rlz_lib::FinancialPing::FormRequest(rlz_lib::TOOLBAR_NOTIFIER, 124 | points, "swg", brand, NULL, NULL, true, &request)); 125 | expected_response.clear(); 126 | base::StringAppendF(&expected_response, 127 | "/tools/pso/ping?as=swg&brand=%s&events=I7S,W1I&rep=2" 128 | "&rlz=T4:" DCC_PARAM, brand); 129 | EXPECT_EQ(expected_response, request); 130 | 131 | 132 | // Clear all events. 133 | EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER)); 134 | 135 | // Clear all RLZs. 136 | char rlz[rlz_lib::kMaxRlzLength + 1]; 137 | for (int ap = rlz_lib::NO_ACCESS_POINT + 1; 138 | ap < rlz_lib::LAST_ACCESS_POINT; ap++) { 139 | rlz[0] = 0; 140 | rlz_lib::AccessPoint point = static_cast(ap); 141 | if (rlz_lib::GetAccessPointRlz(point, rlz, arraysize(rlz)) && rlz[0]) { 142 | rlz_lib::SetAccessPointRlz(point, ""); 143 | } 144 | } 145 | 146 | EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, 147 | "TbRlzValue")); 148 | EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::QUICK_SEARCH_BOX, 149 | "QsbRlzValue")); 150 | EXPECT_TRUE(rlz_lib::FinancialPing::FormRequest(rlz_lib::TOOLBAR_NOTIFIER, 151 | points, "swg", brand, NULL, NULL, false, &request)); 152 | expected_response.clear(); 153 | base::StringAppendF(&expected_response, 154 | "/tools/pso/ping?as=swg&brand=%s&rep=2&rlz=T4:TbRlzValue," 155 | "Q1:QsbRlzValue" DCC_PARAM, brand); 156 | EXPECT_STREQ(expected_response.c_str(), request.c_str()); 157 | 158 | if (!GetAccessPointRlz(rlz_lib::IE_HOME_PAGE, rlz, arraysize(rlz))) { 159 | points[2] = rlz_lib::IE_HOME_PAGE; 160 | EXPECT_TRUE(rlz_lib::FinancialPing::FormRequest(rlz_lib::TOOLBAR_NOTIFIER, 161 | points, "swg", brand, "MyId", "en-US", true, &request)); 162 | expected_response.clear(); 163 | base::StringAppendF(&expected_response, 164 | "/tools/pso/ping?as=swg&brand=%s&hl=en-US&pid=MyId&rep=2" 165 | "&rlz=T4:TbRlzValue,Q1:QsbRlzValue" DCC_PARAM, brand); 166 | EXPECT_STREQ(expected_response.c_str(), request.c_str()); 167 | } 168 | } 169 | 170 | TEST_F(FinancialPingTest, FormRequestBadBrand) { 171 | rlz_lib::AccessPoint points[] = 172 | {rlz_lib::IETB_SEARCH_BOX, rlz_lib::NO_ACCESS_POINT, 173 | rlz_lib::NO_ACCESS_POINT}; 174 | 175 | std::string request; 176 | bool ok = rlz_lib::FinancialPing::FormRequest(rlz_lib::TOOLBAR_NOTIFIER, 177 | points, "swg", "GOOG", NULL, "en", false, &request); 178 | EXPECT_EQ(rlz_lib::SupplementaryBranding::GetBrand().empty(), ok); 179 | } 180 | 181 | 182 | static void SetLastPingTime(int64 time, rlz_lib::Product product) { 183 | rlz_lib::ScopedRlzValueStoreLock lock; 184 | rlz_lib::RlzValueStore* store = lock.GetStore(); 185 | ASSERT_TRUE(store); 186 | ASSERT_TRUE(store->HasAccess(rlz_lib::RlzValueStore::kWriteAccess)); 187 | store->WritePingTime(product, time); 188 | } 189 | 190 | TEST_F(FinancialPingTest, IsPingTime) { 191 | int64 now = GetSystemTimeAsInt64(); 192 | int64 last_ping = now - rlz_lib::kEventsPingInterval - k1MinuteInterval; 193 | SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER); 194 | 195 | // No events, last ping just over a day ago. 196 | EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER)); 197 | EXPECT_FALSE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER, 198 | false)); 199 | 200 | // Has events, last ping just over a day ago. 201 | EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER, 202 | rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE)); 203 | EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER, 204 | false)); 205 | 206 | // Has events, last ping just under a day ago. 207 | last_ping = now - rlz_lib::kEventsPingInterval + k1MinuteInterval; 208 | SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER); 209 | EXPECT_FALSE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER, 210 | false)); 211 | 212 | EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER)); 213 | 214 | // No events, last ping just under a week ago. 215 | last_ping = now - rlz_lib::kNoEventsPingInterval + k1MinuteInterval; 216 | SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER); 217 | EXPECT_FALSE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER, 218 | false)); 219 | 220 | // No events, last ping just over a week ago. 221 | last_ping = now - rlz_lib::kNoEventsPingInterval - k1MinuteInterval; 222 | SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER); 223 | EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER, 224 | false)); 225 | 226 | // Last ping was in future (invalid). 227 | last_ping = now + k1MinuteInterval; 228 | SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER); 229 | EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER, 230 | false)); 231 | EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER, 232 | rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE)); 233 | EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER, 234 | false)); 235 | } 236 | 237 | TEST_F(FinancialPingTest, BrandingIsPingTime) { 238 | // Don't run these tests if a supplementary brand is already in place. That 239 | // way we can control the branding. 240 | if (!rlz_lib::SupplementaryBranding::GetBrand().empty()) 241 | return; 242 | 243 | int64 now = GetSystemTimeAsInt64(); 244 | int64 last_ping = now - rlz_lib::kEventsPingInterval - k1MinuteInterval; 245 | SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER); 246 | 247 | // Has events, last ping just over a day ago. 248 | EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER, 249 | rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE)); 250 | EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER, 251 | false)); 252 | 253 | { 254 | rlz_lib::SupplementaryBranding branding("TEST"); 255 | SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER); 256 | 257 | // Has events, last ping just over a day ago. 258 | EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER, 259 | rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE)); 260 | EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER, 261 | false)); 262 | } 263 | 264 | last_ping = now - k1MinuteInterval; 265 | SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER); 266 | 267 | EXPECT_FALSE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER, 268 | false)); 269 | 270 | { 271 | rlz_lib::SupplementaryBranding branding("TEST"); 272 | EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER, 273 | false)); 274 | } 275 | } 276 | 277 | TEST_F(FinancialPingTest, ClearLastPingTime) { 278 | int64 now = GetSystemTimeAsInt64(); 279 | int64 last_ping = now - rlz_lib::kEventsPingInterval + k1MinuteInterval; 280 | SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER); 281 | 282 | // Has events, last ping just under a day ago. 283 | EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER)); 284 | EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER, 285 | rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE)); 286 | EXPECT_FALSE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER, 287 | false)); 288 | 289 | EXPECT_TRUE(rlz_lib::FinancialPing::ClearLastPingTime( 290 | rlz_lib::TOOLBAR_NOTIFIER)); 291 | EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER, 292 | false)); 293 | } 294 | -------------------------------------------------------------------------------- /lib/lib_values.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // Key and value names of the location of the RLZ shared state. 6 | 7 | #include "rlz/lib/lib_values.h" 8 | 9 | #include "base/stringprintf.h" 10 | #include "rlz/lib/assert.h" 11 | 12 | namespace rlz_lib { 13 | 14 | // 15 | // Ping information. 16 | // 17 | 18 | // rep=2: includes the new stateful events. 19 | const char kProtocolCgiArgument[] = "rep=2"; 20 | 21 | const char kEventsCgiVariable[] = "events"; 22 | const char kStatefulEventsCgiVariable[] = "stateful-events"; 23 | const char kEventsCgiSeparator = ','; 24 | 25 | const char kRlzCgiVariable[] = "rlz"; 26 | const char kRlzCgiSeparator[] = ","; 27 | const char kRlzCgiIndicator[] = ":"; 28 | 29 | const char kProductSignatureCgiVariable[] = "as"; 30 | const char kProductBrandCgiVariable[] = "brand"; 31 | const char kProductLanguageCgiVariable[] = "hl"; 32 | const char kProductIdCgiVariable[] = "pid"; 33 | 34 | const char kDccCgiVariable[] = "dcc"; 35 | const char kRlsCgiVariable[] = "rls"; 36 | const char kMachineIdCgiVariable[] = "id"; 37 | const char kSetDccResponseVariable[] = "set_dcc"; 38 | 39 | // 40 | // Financial server information. 41 | // 42 | 43 | const char kFinancialPingPath[] = "/tools/pso/ping"; 44 | const char kFinancialServer[] = "clients1.google.com"; 45 | const int kFinancialPort = 80; 46 | 47 | // Ping times in 100-nanosecond intervals. 48 | const int64 kEventsPingInterval = 24LL * 3600LL * 10000000LL; // 1 day 49 | const int64 kNoEventsPingInterval = kEventsPingInterval * 7LL; // 1 week 50 | 51 | const char kFinancialPingUserAgent[] = "Mozilla/4.0 (compatible; Win32)"; 52 | const char* kFinancialPingResponseObjects[] = { "text/*", NULL }; 53 | 54 | // 55 | // AccessPoint and Event names. 56 | // 57 | // 58 | 59 | const char* GetAccessPointName(AccessPoint point) { 60 | switch (point) { 61 | case NO_ACCESS_POINT: return ""; 62 | case IE_DEFAULT_SEARCH: return "I7"; 63 | case IE_HOME_PAGE: return "W1"; 64 | case IETB_SEARCH_BOX: return "T4"; 65 | case QUICK_SEARCH_BOX: return "Q1"; 66 | case GD_DESKBAND: return "D1"; 67 | case GD_SEARCH_GADGET: return "D2"; 68 | case GD_WEB_SERVER: return "D3"; 69 | case GD_OUTLOOK: return "D4"; 70 | case CHROME_OMNIBOX: return "C1"; 71 | case CHROME_HOME_PAGE: return "C2"; 72 | case FFTB2_BOX: return "B2"; 73 | case FFTB3_BOX: return "B3"; 74 | case PINYIN_IME_BHO: return "N1"; 75 | case IGOOGLE_WEBPAGE: return "G1"; 76 | case MOBILE_IDLE_SCREEN_BLACKBERRY: return "H1"; 77 | case MOBILE_IDLE_SCREEN_WINMOB: return "H2"; 78 | case MOBILE_IDLE_SCREEN_SYMBIAN: return "H3"; 79 | case FF_HOME_PAGE: return "R0"; 80 | case FF_SEARCH_BOX: return "R1"; 81 | case IE_BROWSED_PAGE: return "R2"; 82 | case QSB_WIN_BOX: return "R3"; 83 | case WEBAPPS_CALENDAR: return "R4"; 84 | case WEBAPPS_DOCS: return "R5"; 85 | case WEBAPPS_GMAIL: return "R6"; 86 | case IETB_LINKDOCTOR: return "R7"; 87 | case FFTB_LINKDOCTOR: return "R8"; 88 | case IETB7_SEARCH_BOX: return "T7"; 89 | case TB8_SEARCH_BOX: return "T8"; 90 | case CHROME_FRAME: return "C3"; 91 | case PARTNER_AP_1: return "V1"; 92 | case PARTNER_AP_2: return "V2"; 93 | case PARTNER_AP_3: return "V3"; 94 | case PARTNER_AP_4: return "V4"; 95 | case PARTNER_AP_5: return "V5"; 96 | case UNDEFINED_AP_H: return "RH"; 97 | case UNDEFINED_AP_I: return "RI"; 98 | case UNDEFINED_AP_J: return "RJ"; 99 | case UNDEFINED_AP_K: return "RK"; 100 | case UNDEFINED_AP_L: return "RL"; 101 | case UNDEFINED_AP_M: return "RM"; 102 | case UNDEFINED_AP_N: return "RN"; 103 | case UNDEFINED_AP_O: return "RO"; 104 | case UNDEFINED_AP_P: return "RP"; 105 | case UNDEFINED_AP_Q: return "RQ"; 106 | case UNDEFINED_AP_R: return "RR"; 107 | case UNDEFINED_AP_S: return "RS"; 108 | case UNDEFINED_AP_T: return "RT"; 109 | case UNDEFINED_AP_U: return "RU"; 110 | case UNDEFINED_AP_V: return "RV"; 111 | case UNDEFINED_AP_W: return "RW"; 112 | case UNDEFINED_AP_X: return "RX"; 113 | case UNDEFINED_AP_Y: return "RY"; 114 | case UNDEFINED_AP_Z: return "RZ"; 115 | case PACK_AP0: return "U0"; 116 | case PACK_AP1: return "U1"; 117 | case PACK_AP2: return "U2"; 118 | case PACK_AP3: return "U3"; 119 | case PACK_AP4: return "U4"; 120 | case PACK_AP5: return "U5"; 121 | case PACK_AP6: return "U6"; 122 | case PACK_AP7: return "U7"; 123 | case PACK_AP8: return "U8"; 124 | case PACK_AP9: return "U9"; 125 | case PACK_AP10: return "UA"; 126 | case PACK_AP11: return "UB"; 127 | case PACK_AP12: return "UC"; 128 | case PACK_AP13: return "UD"; 129 | case LAST_ACCESS_POINT: ; // Fall through. 130 | } 131 | 132 | ASSERT_STRING("GetAccessPointName: Unknown Access Point"); 133 | return NULL; 134 | } 135 | 136 | 137 | bool GetAccessPointFromName(const char* name, AccessPoint* point) { 138 | if (!point) { 139 | ASSERT_STRING("GetAccessPointFromName: point is NULL"); 140 | return false; 141 | } 142 | *point = NO_ACCESS_POINT; 143 | if (!name) 144 | return false; 145 | 146 | for (int i = NO_ACCESS_POINT; i < LAST_ACCESS_POINT; i++) 147 | if (strcmp(name, GetAccessPointName(static_cast(i))) == 0) { 148 | *point = static_cast(i); 149 | return true; 150 | } 151 | 152 | return false; 153 | } 154 | 155 | 156 | const char* GetEventName(Event event) { 157 | switch (event) { 158 | case INVALID_EVENT: return ""; 159 | case INSTALL: return "I"; 160 | case SET_TO_GOOGLE: return "S"; 161 | case FIRST_SEARCH: return "F"; 162 | case REPORT_RLS: return "R"; 163 | case ACTIVATE: return "A"; 164 | case LAST_EVENT: ; // Fall through. 165 | } 166 | 167 | ASSERT_STRING("GetPointName: Unknown Event"); 168 | return NULL; 169 | } 170 | 171 | 172 | bool GetEventFromName(const char* name, Event* event) { 173 | if (!event) { 174 | ASSERT_STRING("GetEventFromName: event is NULL"); 175 | return false; 176 | } 177 | *event = INVALID_EVENT; 178 | if (!name) 179 | return false; 180 | 181 | for (int i = INVALID_EVENT; i < LAST_EVENT; i++) 182 | if (strcmp(name, GetEventName(static_cast(i))) == 0) { 183 | *event = static_cast(i); 184 | return true; 185 | } 186 | 187 | return false; 188 | } 189 | 190 | const char* GetProductName(Product product) { 191 | switch (product) { 192 | case IE_TOOLBAR: return "T"; 193 | case TOOLBAR_NOTIFIER: return "P"; 194 | case PACK: return "U"; 195 | case DESKTOP: return "D"; 196 | case CHROME: return "C"; 197 | case FF_TOOLBAR: return "B"; 198 | case QSB_WIN: return "K"; 199 | case WEBAPPS: return "W"; 200 | case PINYIN_IME: return "N"; 201 | case PARTNER: return "V"; 202 | } 203 | 204 | ASSERT_STRING("GetProductName: Unknown Product"); 205 | return ""; 206 | } 207 | 208 | } // namespace rlz_lib 209 | -------------------------------------------------------------------------------- /lib/lib_values.h: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // Key and value names of the location of the RLZ shared state. 6 | 7 | #ifndef RLZ_LIB_LIB_VALUES_H_ 8 | #define RLZ_LIB_LIB_VALUES_H_ 9 | 10 | #include "base/basictypes.h" 11 | #include "rlz/lib/rlz_enums.h" 12 | 13 | namespace rlz_lib { 14 | 15 | // 16 | // Ping CGI arguments: 17 | // 18 | // Events are reported as (without spaces): 19 | // kEventsCgiVariable = kEventsCgiSeparator ... 20 | // 21 | // Event responses from the server look like: 22 | // kEventsCgiVariable : kEventsCgiSeparator ... 23 | // 24 | // RLZ's are reported as (without spaces): 25 | // kRlzCgiVariable = 26 | // .... 27 | // 28 | // RLZ responses from the server look like (without spaces): 29 | // kRlzCgiVariable : 30 | // 31 | // DCC if reported should look like (without spaces): 32 | // kDccCgiVariable = 33 | // 34 | // RLS if reported should look like (without spaces): 35 | // kRlsCgiVariable = 36 | // 37 | // Machine ID if reported should look like (without spaces): 38 | // kMachineIdCgiVariable = 39 | // 40 | // A server response setting / confirming the DCC will look like (no spaces): 41 | // kDccCgiVariable : 42 | // 43 | // Each ping to the server must also contain kProtocolCgiArgument as well. 44 | // 45 | // Pings may also contain (but not necessarily controlled by this Lib): 46 | // - The product signature: kProductSignatureCgiVariable = 47 | // - The product brand: kProductBrandCgiVariable = 48 | // - The product installation ID: kProductIdCgiVariable = 49 | extern const char kEventsCgiVariable[]; 50 | extern const char kStatefulEventsCgiVariable[]; 51 | extern const char kEventsCgiSeparator; 52 | 53 | extern const char kDccCgiVariable[]; 54 | extern const char kProtocolCgiArgument[]; 55 | 56 | extern const char kProductSignatureCgiVariable[]; 57 | extern const char kProductBrandCgiVariable[]; 58 | extern const char kProductLanguageCgiVariable[]; 59 | extern const char kProductIdCgiVariable[]; 60 | 61 | extern const char kRlzCgiVariable[]; 62 | extern const char kRlzCgiSeparator[]; 63 | extern const char kRlzCgiIndicator[]; 64 | 65 | extern const char kRlsCgiVariable[]; 66 | extern const char kMachineIdCgiVariable[]; 67 | extern const char kSetDccResponseVariable[]; 68 | 69 | // 70 | // Financial ping server information. 71 | // 72 | 73 | extern const char kFinancialPingPath[]; 74 | extern const char kFinancialServer[]; 75 | 76 | extern const int kFinancialPort; 77 | 78 | extern const int64 kEventsPingInterval; 79 | extern const int64 kNoEventsPingInterval; 80 | 81 | extern const char kFinancialPingUserAgent[]; 82 | extern const char* kFinancialPingResponseObjects[]; 83 | 84 | // 85 | // The names for AccessPoints and Events that we use MUST be the same 86 | // as those used/understood by the server. 87 | // 88 | const char* GetAccessPointName(AccessPoint point); 89 | bool GetAccessPointFromName(const char* name, AccessPoint* point); 90 | 91 | const char* GetEventName(Event event); 92 | bool GetEventFromName(const char* name, Event* event); 93 | 94 | // The names for products are used only client-side. 95 | const char* GetProductName(Product product); 96 | 97 | } // namespace rlz_lib 98 | 99 | #endif // RLZ_LIB_LIB_VALUES_H_ 100 | -------------------------------------------------------------------------------- /lib/lib_values_unittest.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | 5 | #include "rlz/lib/lib_values.h" 6 | 7 | #include "base/logging.h" 8 | #include "rlz/lib/assert.h" 9 | #include "testing/gmock/include/gmock/gmock.h" 10 | #include "testing/gtest/include/gtest/gtest.h" 11 | 12 | TEST(LibValuesUnittest, GetAccessPointFromName) { 13 | rlz_lib::SetExpectedAssertion("GetAccessPointFromName: point is NULL"); 14 | EXPECT_FALSE(rlz_lib::GetAccessPointFromName("", NULL)); 15 | rlz_lib::SetExpectedAssertion(""); 16 | 17 | rlz_lib::AccessPoint point; 18 | EXPECT_FALSE(rlz_lib::GetAccessPointFromName(NULL, &point)); 19 | EXPECT_EQ(rlz_lib::NO_ACCESS_POINT, point); 20 | 21 | EXPECT_TRUE(rlz_lib::GetAccessPointFromName("", &point)); 22 | EXPECT_EQ(rlz_lib::NO_ACCESS_POINT, point); 23 | 24 | EXPECT_FALSE(rlz_lib::GetAccessPointFromName("i1", &point)); 25 | EXPECT_EQ(rlz_lib::NO_ACCESS_POINT, point); 26 | 27 | EXPECT_TRUE(rlz_lib::GetAccessPointFromName("I7", &point)); 28 | EXPECT_EQ(rlz_lib::IE_DEFAULT_SEARCH, point); 29 | 30 | EXPECT_TRUE(rlz_lib::GetAccessPointFromName("T4", &point)); 31 | EXPECT_EQ(rlz_lib::IETB_SEARCH_BOX, point); 32 | 33 | EXPECT_FALSE(rlz_lib::GetAccessPointFromName("T4 ", &point)); 34 | EXPECT_EQ(rlz_lib::NO_ACCESS_POINT, point); 35 | } 36 | 37 | 38 | TEST(LibValuesUnittest, GetEventFromName) { 39 | rlz_lib::SetExpectedAssertion("GetEventFromName: event is NULL"); 40 | EXPECT_FALSE(rlz_lib::GetEventFromName("", NULL)); 41 | rlz_lib::SetExpectedAssertion(""); 42 | 43 | rlz_lib::Event event; 44 | EXPECT_FALSE(rlz_lib::GetEventFromName(NULL, &event)); 45 | EXPECT_EQ(rlz_lib::INVALID_EVENT, event); 46 | 47 | EXPECT_TRUE(rlz_lib::GetEventFromName("", &event)); 48 | EXPECT_EQ(rlz_lib::INVALID_EVENT, event); 49 | 50 | EXPECT_FALSE(rlz_lib::GetEventFromName("i1", &event)); 51 | EXPECT_EQ(rlz_lib::INVALID_EVENT, event); 52 | 53 | EXPECT_TRUE(rlz_lib::GetEventFromName("I", &event)); 54 | EXPECT_EQ(rlz_lib::INSTALL, event); 55 | 56 | EXPECT_TRUE(rlz_lib::GetEventFromName("F", &event)); 57 | EXPECT_EQ(rlz_lib::FIRST_SEARCH, event); 58 | 59 | EXPECT_FALSE(rlz_lib::GetEventFromName("F ", &event)); 60 | EXPECT_EQ(rlz_lib::INVALID_EVENT, event); 61 | } 62 | -------------------------------------------------------------------------------- /lib/machine_id.cc: -------------------------------------------------------------------------------- 1 | #include "rlz/lib/machine_id.h" 2 | 3 | #include "base/sha1.h" 4 | #include "rlz/lib/assert.h" 5 | #include "rlz/lib/crc8.h" 6 | #include "rlz/lib/string_utils.h" 7 | 8 | namespace rlz_lib { 9 | 10 | bool GetMachineId(std::string* machine_id) { 11 | if (!machine_id) 12 | return false; 13 | 14 | static std::string calculated_id; 15 | static bool calculated = false; 16 | if (calculated) { 17 | *machine_id = calculated_id; 18 | return true; 19 | } 20 | 21 | string16 sid_string; 22 | int volume_id; 23 | if (!GetRawMachineId(&sid_string, &volume_id)) 24 | return false; 25 | 26 | if (!testing::GetMachineIdImpl(sid_string, volume_id, machine_id)) 27 | return false; 28 | 29 | calculated = true; 30 | calculated_id = *machine_id; 31 | return true; 32 | } 33 | 34 | namespace testing { 35 | 36 | bool GetMachineIdImpl(const string16& sid_string, 37 | int volume_id, 38 | std::string* machine_id) { 39 | machine_id->clear(); 40 | 41 | // The ID should be the SID hash + the Hard Drive SNo. + checksum byte. 42 | static const int kSizeWithoutChecksum = base::kSHA1Length + sizeof(int); 43 | std::basic_string id_binary(kSizeWithoutChecksum + 1, 0); 44 | 45 | if (!sid_string.empty()) { 46 | // In order to be compatible with the old version of RLZ, the hash of the 47 | // SID must be done with all the original bytes from the unicode string. 48 | // However, the chromebase SHA1 hash function takes only an std::string as 49 | // input, so the unicode string needs to be converted to std::string 50 | // "as is". 51 | size_t byte_count = sid_string.size() * sizeof(string16::value_type); 52 | const char* buffer = reinterpret_cast(sid_string.c_str()); 53 | std::string sid_string_buffer(buffer, byte_count); 54 | 55 | // Note that digest can have embedded nulls. 56 | std::string digest(base::SHA1HashString(sid_string_buffer)); 57 | VERIFY(digest.size() == base::kSHA1Length); 58 | std::copy(digest.begin(), digest.end(), id_binary.begin()); 59 | } 60 | 61 | // Convert from int to binary (makes big-endian). 62 | for (size_t i = 0; i < sizeof(int); i++) { 63 | int shift_bits = 8 * (sizeof(int) - i - 1); 64 | id_binary[base::kSHA1Length + i] = static_cast( 65 | (volume_id >> shift_bits) & 0xFF); 66 | } 67 | 68 | // Append the checksum byte. 69 | if (!sid_string.empty() || (0 != volume_id)) 70 | rlz_lib::Crc8::Generate(id_binary.c_str(), 71 | kSizeWithoutChecksum, 72 | &id_binary[kSizeWithoutChecksum]); 73 | 74 | return rlz_lib::BytesToString( 75 | id_binary.c_str(), kSizeWithoutChecksum + 1, machine_id); 76 | } 77 | 78 | } // namespace testing 79 | 80 | } // namespace rlz_lib 81 | -------------------------------------------------------------------------------- /lib/machine_id.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | 5 | #ifndef RLZ_LIB_MACHINE_ID_H_ 6 | #define RLZ_LIB_MACHINE_ID_H_ 7 | 8 | #include "base/string16.h" 9 | 10 | #include 11 | 12 | namespace rlz_lib { 13 | 14 | // Gets the unique ID for the machine used for RLZ tracking purposes. On 15 | // Windows, this ID is derived from the Windows machine SID, and is the string 16 | // representation of a 20 byte hash + 4 bytes volum id + a 1 byte checksum. 17 | // Included in financial pings with events, unless explicitly forbidden by the 18 | // calling application. 19 | bool GetMachineId(std::string* machine_id); 20 | 21 | // Retrieves a raw machine identifier string and a machine-specific 22 | // 4 byte value. GetMachineId() will SHA1 |data|, append |more_data|, compute 23 | // the Crc8 of that, and return a hex-encoded string of that data. 24 | bool GetRawMachineId(string16* data, int* more_data); 25 | 26 | namespace testing { 27 | bool GetMachineIdImpl(const string16& sid_string, 28 | int volume_id, 29 | std::string* machine_id); 30 | } // namespace testing 31 | 32 | } // namespace rlz_lib 33 | 34 | #endif // RLZ_LIB_MACHINE_ID_H_ 35 | -------------------------------------------------------------------------------- /lib/machine_id_unittest.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | 5 | #include "rlz/lib/machine_id.h" 6 | 7 | #include "base/string16.h" 8 | #include "base/utf_string_conversions.h" 9 | #include "rlz/test/rlz_test_helpers.h" 10 | #include "testing/gtest/include/gtest/gtest.h" 11 | 12 | // This test will fail if the behavior of GetMachineId changes. 13 | TEST(MachineDealCodeTestMachineId, MachineId) { 14 | string16 computer_sid(UTF8ToUTF16( 15 | "S-1-5-21-2345599882-2448789067-1921365677")); 16 | std::string id; 17 | rlz_lib::testing::GetMachineIdImpl(computer_sid, 2651229008, &id); 18 | EXPECT_STREQ("A341BA986A7E86840688977FCF20C86E253F00919E068B50F8", 19 | id.c_str()); 20 | } 21 | -------------------------------------------------------------------------------- /lib/rlz_enums.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | 5 | #ifndef RLZ_LIB_RLZ_ENUMS_H_ 6 | #define RLZ_LIB_RLZ_ENUMS_H_ 7 | 8 | namespace rlz_lib { 9 | 10 | // An Access Point offers a way to search using Google. 11 | enum AccessPoint { 12 | NO_ACCESS_POINT = 0, 13 | 14 | // Access points on Windows PCs. 15 | IE_DEFAULT_SEARCH, // The IE7+ chrome search box next to the address bar. 16 | IE_HOME_PAGE, // Search box on IE 5+ primary home page when Google. 17 | IETB_SEARCH_BOX, // IE Toolbar v4+ search box. 18 | QUICK_SEARCH_BOX, // Search box brought up by ctrl-ctrl key sequence, 19 | // distributed as a part of Google Desktop 20 | GD_DESKBAND, // Search box in deskbar when GD in deskbar mode. 21 | GD_SEARCH_GADGET, // Search gadget when GD in sidebar mode. 22 | GD_WEB_SERVER, // Boxes in web pages shown by local GD web server. 23 | GD_OUTLOOK, // Search box installed within outlook by GD. 24 | CHROME_OMNIBOX, // Chrome searches through the address bar omnibox. 25 | CHROME_HOME_PAGE, // Chrome searches through Google as home page. 26 | FFTB2_BOX, // Firefox Toolbar v2 Search Box. 27 | FFTB3_BOX, // Firefox Toolbar v3+ Search Box. 28 | PINYIN_IME_BHO, // Goopy Input Method Editor BHO (Pinyin). 29 | IGOOGLE_WEBPAGE, // Searches on iGoogle through partner deals. 30 | 31 | // Mobile idle screen search for different platforms. 32 | MOBILE_IDLE_SCREEN_BLACKBERRY, 33 | MOBILE_IDLE_SCREEN_WINMOB, 34 | MOBILE_IDLE_SCREEN_SYMBIAN, 35 | 36 | FF_HOME_PAGE, // Firefox home page when set to Google. 37 | FF_SEARCH_BOX, // Firefox search box when set to Google. 38 | IE_BROWSED_PAGE, // Search made in IE through user action (no product). 39 | QSB_WIN_BOX, // Search box brought up by ctrl+space by default, 40 | // distributed by toolbar and separate from the GD QSB 41 | WEBAPPS_CALENDAR, // Webapps use of calendar. 42 | WEBAPPS_DOCS, // Webapps use of writely. 43 | WEBAPPS_GMAIL, // Webapps use of Gmail. 44 | 45 | IETB_LINKDOCTOR, // Linkdoctor of IE Toolbar 46 | FFTB_LINKDOCTOR, // Linkdoctor of FF Toolbar 47 | IETB7_SEARCH_BOX, // IE Toolbar search box. 48 | TB8_SEARCH_BOX, // IE/FF Toolbar search box. 49 | CHROME_FRAME, // Chrome Frame. 50 | 51 | // Partner access points. 52 | PARTNER_AP_1, 53 | PARTNER_AP_2, 54 | PARTNER_AP_3, 55 | PARTNER_AP_4, 56 | PARTNER_AP_5, 57 | 58 | // Unclaimed access points - should be used first before creating new APs. 59 | // Please also make sure you re-name the enum before using an unclaimed value; 60 | // this acts as a check to ensure we don't have collisions. 61 | UNDEFINED_AP_H, 62 | UNDEFINED_AP_I, 63 | UNDEFINED_AP_J, 64 | UNDEFINED_AP_K, 65 | UNDEFINED_AP_L, 66 | UNDEFINED_AP_M, 67 | UNDEFINED_AP_N, 68 | UNDEFINED_AP_O, 69 | UNDEFINED_AP_P, 70 | UNDEFINED_AP_Q, 71 | UNDEFINED_AP_R, 72 | UNDEFINED_AP_S, 73 | UNDEFINED_AP_T, 74 | UNDEFINED_AP_U, 75 | UNDEFINED_AP_V, 76 | UNDEFINED_AP_W, 77 | UNDEFINED_AP_X, 78 | UNDEFINED_AP_Y, 79 | UNDEFINED_AP_Z, 80 | 81 | PACK_AP0, 82 | PACK_AP1, 83 | PACK_AP2, 84 | PACK_AP3, 85 | PACK_AP4, 86 | PACK_AP5, 87 | PACK_AP6, 88 | PACK_AP7, 89 | PACK_AP8, 90 | PACK_AP9, 91 | PACK_AP10, 92 | PACK_AP11, 93 | PACK_AP12, 94 | PACK_AP13, 95 | 96 | // New Access Points should be added here without changing existing enums, 97 | // (i.e. before LAST_ACCESS_POINT) 98 | LAST_ACCESS_POINT 99 | }; 100 | 101 | // A product is an entity which wants to gets credit for setting 102 | // an Access Point. 103 | enum Product { 104 | IE_TOOLBAR = 1, 105 | TOOLBAR_NOTIFIER, 106 | PACK, 107 | DESKTOP, 108 | CHROME, 109 | FF_TOOLBAR, 110 | QSB_WIN, 111 | WEBAPPS, 112 | PINYIN_IME, 113 | PARTNER 114 | // New Products should be added here without changing existing enums. 115 | }; 116 | 117 | // Events that note Product and Access Point modifications. 118 | enum Event { 119 | INVALID_EVENT = 0, 120 | INSTALL = 1, // Access Point added to the system. 121 | SET_TO_GOOGLE, // Point set from non-Google provider to Google. 122 | FIRST_SEARCH, // First search from point since INSTALL 123 | REPORT_RLS, // Report old system "RLS" financial value for this point. 124 | // New Events should be added here without changing existing enums, 125 | // before LAST_EVENT. 126 | ACTIVATE, // Product being used for a period of time. 127 | LAST_EVENT 128 | }; 129 | 130 | } // namespace rlz_lib 131 | 132 | #endif // RLZ_LIB_RLZ_ENUMS_H_ 133 | -------------------------------------------------------------------------------- /lib/rlz_lib.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // A library to manage RLZ information for access-points shared 6 | // across different client applications. 7 | 8 | #include "rlz/lib/rlz_lib.h" 9 | 10 | #include "base/string_util.h" 11 | #include "base/stringprintf.h" 12 | #include "rlz/lib/assert.h" 13 | #include "rlz/lib/crc32.h" 14 | #include "rlz/lib/financial_ping.h" 15 | #include "rlz/lib/lib_values.h" 16 | #include "rlz/lib/rlz_value_store.h" 17 | #include "rlz/lib/string_utils.h" 18 | 19 | namespace { 20 | 21 | // Event information returned from ping response. 22 | struct ReturnedEvent { 23 | rlz_lib::AccessPoint access_point; 24 | rlz_lib::Event event_type; 25 | }; 26 | 27 | // Helper functions 28 | 29 | bool IsAccessPointSupported(rlz_lib::AccessPoint point) { 30 | switch (point) { 31 | case rlz_lib::NO_ACCESS_POINT: 32 | case rlz_lib::LAST_ACCESS_POINT: 33 | 34 | case rlz_lib::MOBILE_IDLE_SCREEN_BLACKBERRY: 35 | case rlz_lib::MOBILE_IDLE_SCREEN_WINMOB: 36 | case rlz_lib::MOBILE_IDLE_SCREEN_SYMBIAN: 37 | // These AP's are never available on Windows PCs. 38 | return false; 39 | 40 | case rlz_lib::IE_DEFAULT_SEARCH: 41 | case rlz_lib::IE_HOME_PAGE: 42 | case rlz_lib::IETB_SEARCH_BOX: 43 | case rlz_lib::QUICK_SEARCH_BOX: 44 | case rlz_lib::GD_DESKBAND: 45 | case rlz_lib::GD_SEARCH_GADGET: 46 | case rlz_lib::GD_WEB_SERVER: 47 | case rlz_lib::GD_OUTLOOK: 48 | case rlz_lib::CHROME_OMNIBOX: 49 | case rlz_lib::CHROME_HOME_PAGE: 50 | // TODO: Figure out when these settings are set to Google. 51 | 52 | default: 53 | return true; 54 | } 55 | } 56 | 57 | // Current RLZ can only use [a-zA-Z0-9_\-] 58 | // We will be more liberal and allow some additional chars, but not url meta 59 | // chars. 60 | bool IsGoodRlzChar(const char ch) { 61 | if (IsAsciiAlpha(ch) || IsAsciiDigit(ch)) 62 | return true; 63 | 64 | switch (ch) { 65 | case '_': 66 | case '-': 67 | case '!': 68 | case '@': 69 | case '$': 70 | case '*': 71 | case '(': 72 | case ')': 73 | case ';': 74 | case '.': 75 | case '<': 76 | case '>': 77 | return true; 78 | } 79 | 80 | return false; 81 | } 82 | 83 | // This function will remove bad rlz chars and also limit the max rlz to some 84 | // reasonable size. It also assumes that normalized_rlz is at least 85 | // kMaxRlzLength+1 long. 86 | void NormalizeRlz(const char* raw_rlz, char* normalized_rlz) { 87 | int index = 0; 88 | for (; raw_rlz[index] != 0 && index < rlz_lib::kMaxRlzLength; ++index) { 89 | char current = raw_rlz[index]; 90 | if (IsGoodRlzChar(current)) { 91 | normalized_rlz[index] = current; 92 | } else { 93 | normalized_rlz[index] = '.'; 94 | } 95 | } 96 | 97 | normalized_rlz[index] = 0; 98 | } 99 | 100 | void GetEventsFromResponseString( 101 | const std::string& response_line, 102 | const std::string& field_header, 103 | std::vector* event_array) { 104 | // Get the string of events. 105 | std::string events = response_line.substr(field_header.size()); 106 | TrimWhitespaceASCII(events, TRIM_LEADING, &events); 107 | 108 | int events_length = events.find_first_of("\r\n "); 109 | if (events_length < 0) 110 | events_length = events.size(); 111 | events = events.substr(0, events_length); 112 | 113 | // Break this up into individual events 114 | int event_end_index = -1; 115 | do { 116 | int event_begin = event_end_index + 1; 117 | event_end_index = events.find(rlz_lib::kEventsCgiSeparator, event_begin); 118 | int event_end = event_end_index; 119 | if (event_end < 0) 120 | event_end = events_length; 121 | 122 | std::string event_string = events.substr(event_begin, 123 | event_end - event_begin); 124 | if (event_string.size() != 3) // 3 = 2(AP) + 1(E) 125 | continue; 126 | 127 | rlz_lib::AccessPoint point = rlz_lib::NO_ACCESS_POINT; 128 | rlz_lib::Event event = rlz_lib::INVALID_EVENT; 129 | if (!GetAccessPointFromName(event_string.substr(0, 2).c_str(), &point) || 130 | point == rlz_lib::NO_ACCESS_POINT) { 131 | continue; 132 | } 133 | 134 | if (!GetEventFromName(event_string.substr(event_string.size() - 1).c_str(), 135 | &event) || event == rlz_lib::INVALID_EVENT) { 136 | continue; 137 | } 138 | 139 | ReturnedEvent current_event = {point, event}; 140 | event_array->push_back(current_event); 141 | } while (event_end_index >= 0); 142 | } 143 | 144 | // Event storage functions. 145 | bool RecordStatefulEvent(rlz_lib::Product product, rlz_lib::AccessPoint point, 146 | rlz_lib::Event event) { 147 | rlz_lib::ScopedRlzValueStoreLock lock; 148 | rlz_lib::RlzValueStore* store = lock.GetStore(); 149 | if (!store || !store->HasAccess(rlz_lib::RlzValueStore::kWriteAccess)) 150 | return false; 151 | 152 | // Write the new event to the value store. 153 | const char* point_name = GetAccessPointName(point); 154 | const char* event_name = GetEventName(event); 155 | if (!point_name || !event_name) 156 | return false; 157 | 158 | if (!point_name[0] || !event_name[0]) 159 | return false; 160 | 161 | std::string new_event_value; 162 | base::StringAppendF(&new_event_value, "%s%s", point_name, event_name); 163 | return store->AddStatefulEvent(product, new_event_value.c_str()); 164 | } 165 | 166 | bool GetProductEventsAsCgiHelper(rlz_lib::Product product, char* cgi, 167 | size_t cgi_size, 168 | rlz_lib::RlzValueStore* store) { 169 | // Prepend the CGI param key to the buffer. 170 | std::string cgi_arg; 171 | base::StringAppendF(&cgi_arg, "%s=", rlz_lib::kEventsCgiVariable); 172 | if (cgi_size <= cgi_arg.size()) 173 | return false; 174 | 175 | size_t index; 176 | for (index = 0; index < cgi_arg.size(); ++index) 177 | cgi[index] = cgi_arg[index]; 178 | 179 | // Read stored events. 180 | std::vector events; 181 | if (!store->ReadProductEvents(product, &events)) 182 | return false; 183 | 184 | // Append the events to the buffer. 185 | size_t num_values = 0; 186 | 187 | for (num_values = 0; num_values < events.size(); ++num_values) { 188 | cgi[index] = '\0'; 189 | 190 | int divider = num_values > 0 ? 1 : 0; 191 | int size = cgi_size - (index + divider); 192 | if (size <= 0) 193 | return cgi_size >= (rlz_lib::kMaxCgiLength + 1); 194 | 195 | strncpy(cgi + index + divider, events[num_values].c_str(), size); 196 | if (divider) 197 | cgi[index] = rlz_lib::kEventsCgiSeparator; 198 | 199 | index += std::min((int)events[num_values].length(), size) + divider; 200 | } 201 | 202 | cgi[index] = '\0'; 203 | 204 | return num_values > 0; 205 | } 206 | 207 | } // namespace 208 | 209 | namespace rlz_lib { 210 | 211 | #if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET) 212 | bool SetURLRequestContext(net::URLRequestContextGetter* context) { 213 | return FinancialPing::SetURLRequestContext(context); 214 | } 215 | #endif 216 | 217 | bool GetProductEventsAsCgi(Product product, char* cgi, size_t cgi_size) { 218 | if (!cgi || cgi_size <= 0) { 219 | ASSERT_STRING("GetProductEventsAsCgi: Invalid buffer"); 220 | return false; 221 | } 222 | 223 | cgi[0] = 0; 224 | 225 | ScopedRlzValueStoreLock lock; 226 | RlzValueStore* store = lock.GetStore(); 227 | if (!store || !store->HasAccess(RlzValueStore::kReadAccess)) 228 | return false; 229 | 230 | size_t size_local = std::min( 231 | static_cast(kMaxCgiLength + 1), cgi_size); 232 | bool result = GetProductEventsAsCgiHelper(product, cgi, size_local, store); 233 | 234 | if (!result) { 235 | ASSERT_STRING("GetProductEventsAsCgi: Possibly insufficient buffer size"); 236 | cgi[0] = 0; 237 | return false; 238 | } 239 | 240 | return true; 241 | } 242 | 243 | bool RecordProductEvent(Product product, AccessPoint point, Event event) { 244 | ScopedRlzValueStoreLock lock; 245 | RlzValueStore* store = lock.GetStore(); 246 | if (!store || !store->HasAccess(RlzValueStore::kWriteAccess)) 247 | return false; 248 | 249 | // Get this event's value. 250 | const char* point_name = GetAccessPointName(point); 251 | const char* event_name = GetEventName(event); 252 | if (!point_name || !event_name) 253 | return false; 254 | 255 | if (!point_name[0] || !event_name[0]) 256 | return false; 257 | 258 | std::string new_event_value; 259 | base::StringAppendF(&new_event_value, "%s%s", point_name, event_name); 260 | 261 | // Check whether this event is a stateful event. If so, don't record it. 262 | if (store->IsStatefulEvent(product, new_event_value.c_str())) { 263 | // For a stateful event we skip recording, this function is also 264 | // considered successful. 265 | return true; 266 | } 267 | 268 | // Write the new event to the value store. 269 | return store->AddProductEvent(product, new_event_value.c_str()); 270 | } 271 | 272 | bool ClearProductEvent(Product product, AccessPoint point, Event event) { 273 | ScopedRlzValueStoreLock lock; 274 | RlzValueStore* store = lock.GetStore(); 275 | if (!store || !store->HasAccess(RlzValueStore::kWriteAccess)) 276 | return false; 277 | 278 | // Get the event's value store value and delete it. 279 | const char* point_name = GetAccessPointName(point); 280 | const char* event_name = GetEventName(event); 281 | if (!point_name || !event_name) 282 | return false; 283 | 284 | if (!point_name[0] || !event_name[0]) 285 | return false; 286 | 287 | std::string event_value; 288 | base::StringAppendF(&event_value, "%s%s", point_name, event_name); 289 | return store->ClearProductEvent(product, event_value.c_str()); 290 | } 291 | 292 | // RLZ storage functions. 293 | 294 | bool GetAccessPointRlz(AccessPoint point, char* rlz, size_t rlz_size) { 295 | if (!rlz || rlz_size <= 0) { 296 | ASSERT_STRING("GetAccessPointRlz: Invalid buffer"); 297 | return false; 298 | } 299 | 300 | rlz[0] = 0; 301 | 302 | ScopedRlzValueStoreLock lock; 303 | RlzValueStore* store = lock.GetStore(); 304 | if (!store || !store->HasAccess(RlzValueStore::kReadAccess)) 305 | return false; 306 | 307 | if (!IsAccessPointSupported(point)) 308 | return false; 309 | 310 | return store->ReadAccessPointRlz(point, rlz, rlz_size); 311 | } 312 | 313 | bool SetAccessPointRlz(AccessPoint point, const char* new_rlz) { 314 | ScopedRlzValueStoreLock lock; 315 | RlzValueStore* store = lock.GetStore(); 316 | if (!store || !store->HasAccess(RlzValueStore::kWriteAccess)) 317 | return false; 318 | 319 | if (!new_rlz) { 320 | ASSERT_STRING("SetAccessPointRlz: Invalid buffer"); 321 | return false; 322 | } 323 | 324 | // Return false if the access point is not set to Google. 325 | if (!IsAccessPointSupported(point)) { 326 | ASSERT_STRING(("SetAccessPointRlz: " 327 | "Cannot set RLZ for unsupported access point.")); 328 | return false; 329 | } 330 | 331 | // Verify the RLZ length. 332 | size_t rlz_length = strlen(new_rlz); 333 | if (rlz_length > kMaxRlzLength) { 334 | ASSERT_STRING("SetAccessPointRlz: RLZ length is exceeds max allowed."); 335 | return false; 336 | } 337 | 338 | char normalized_rlz[kMaxRlzLength + 1]; 339 | NormalizeRlz(new_rlz, normalized_rlz); 340 | VERIFY(strlen(new_rlz) == rlz_length); 341 | 342 | // Setting RLZ to empty == clearing. 343 | if (normalized_rlz[0] == 0) 344 | return store->ClearAccessPointRlz(point); 345 | return store->WriteAccessPointRlz(point, normalized_rlz); 346 | } 347 | 348 | // Financial Server pinging functions. 349 | 350 | bool FormFinancialPingRequest(Product product, const AccessPoint* access_points, 351 | const char* product_signature, 352 | const char* product_brand, 353 | const char* product_id, 354 | const char* product_lang, 355 | bool exclude_machine_id, 356 | char* request, size_t request_buffer_size) { 357 | if (!request || request_buffer_size == 0) 358 | return false; 359 | 360 | request[0] = 0; 361 | 362 | std::string request_string; 363 | if (!FinancialPing::FormRequest(product, access_points, product_signature, 364 | product_brand, product_id, product_lang, 365 | exclude_machine_id, &request_string)) 366 | return false; 367 | 368 | if (request_string.size() >= request_buffer_size) 369 | return false; 370 | 371 | strncpy(request, request_string.c_str(), request_buffer_size); 372 | request[request_buffer_size - 1] = 0; 373 | return true; 374 | } 375 | 376 | bool PingFinancialServer(Product product, const char* request, char* response, 377 | size_t response_buffer_size) { 378 | if (!response || response_buffer_size == 0) 379 | return false; 380 | response[0] = 0; 381 | 382 | // Check if the time is right to ping. 383 | if (!FinancialPing::IsPingTime(product, false)) 384 | return false; 385 | 386 | // Send out the ping. 387 | std::string response_string; 388 | if (!FinancialPing::PingServer(request, &response_string)) 389 | return false; 390 | 391 | if (response_string.size() >= response_buffer_size) 392 | return false; 393 | 394 | strncpy(response, response_string.c_str(), response_buffer_size); 395 | response[response_buffer_size - 1] = 0; 396 | return true; 397 | } 398 | 399 | bool IsPingResponseValid(const char* response, int* checksum_idx) { 400 | if (!response || !response[0]) 401 | return false; 402 | 403 | if (checksum_idx) 404 | *checksum_idx = -1; 405 | 406 | if (strlen(response) > kMaxPingResponseLength) { 407 | ASSERT_STRING("IsPingResponseValid: response is too long to parse."); 408 | return false; 409 | } 410 | 411 | // Find the checksum line. 412 | std::string response_string(response); 413 | 414 | std::string checksum_param("\ncrc32: "); 415 | int calculated_crc; 416 | int checksum_index = response_string.find(checksum_param); 417 | if (checksum_index >= 0) { 418 | // Calculate checksum of message preceeding checksum line. 419 | // (+ 1 to include the \n) 420 | std::string message(response_string.substr(0, checksum_index + 1)); 421 | if (!Crc32(message.c_str(), &calculated_crc)) 422 | return false; 423 | } else { 424 | checksum_param = "crc32: "; // Empty response case. 425 | if (!StartsWithASCII(response_string, checksum_param, true)) 426 | return false; 427 | 428 | checksum_index = 0; 429 | if (!Crc32("", &calculated_crc)) 430 | return false; 431 | } 432 | 433 | // Find the checksum value on the response. 434 | int checksum_end = response_string.find("\n", checksum_index + 1); 435 | if (checksum_end < 0) 436 | checksum_end = response_string.size(); 437 | 438 | int checksum_begin = checksum_index + checksum_param.size(); 439 | std::string checksum = response_string.substr(checksum_begin, 440 | checksum_end - checksum_begin + 1); 441 | TrimWhitespaceASCII(checksum, TRIM_ALL, &checksum); 442 | 443 | if (checksum_idx) 444 | *checksum_idx = checksum_index; 445 | 446 | return calculated_crc == HexStringToInteger(checksum.c_str()); 447 | } 448 | 449 | // Complex helpers built on top of other functions. 450 | 451 | bool ParseFinancialPingResponse(Product product, const char* response) { 452 | // Update the last ping time irrespective of success. 453 | FinancialPing::UpdateLastPingTime(product); 454 | // Parse the ping response - update RLZs, clear events. 455 | return ParsePingResponse(product, response); 456 | } 457 | 458 | bool SendFinancialPing(Product product, const AccessPoint* access_points, 459 | const char* product_signature, 460 | const char* product_brand, 461 | const char* product_id, const char* product_lang, 462 | bool exclude_machine_id) { 463 | return SendFinancialPing(product, access_points, product_signature, 464 | product_brand, product_id, product_lang, 465 | exclude_machine_id, false); 466 | } 467 | 468 | 469 | bool SendFinancialPing(Product product, const AccessPoint* access_points, 470 | const char* product_signature, 471 | const char* product_brand, 472 | const char* product_id, const char* product_lang, 473 | bool exclude_machine_id, 474 | const bool skip_time_check) { 475 | // Create the financial ping request. 476 | std::string request; 477 | if (!FinancialPing::FormRequest(product, access_points, product_signature, 478 | product_brand, product_id, product_lang, 479 | exclude_machine_id, &request)) 480 | return false; 481 | 482 | // Check if the time is right to ping. 483 | if (!FinancialPing::IsPingTime(product, skip_time_check)) 484 | return false; 485 | 486 | // Send out the ping, update the last ping time irrespective of success. 487 | FinancialPing::UpdateLastPingTime(product); 488 | std::string response; 489 | if (!FinancialPing::PingServer(request.c_str(), &response)) 490 | return false; 491 | 492 | // Parse the ping response - update RLZs, clear events. 493 | return ParsePingResponse(product, response.c_str()); 494 | } 495 | 496 | // TODO: Use something like RSA to make sure the response is 497 | // from a Google server. 498 | bool ParsePingResponse(Product product, const char* response) { 499 | rlz_lib::ScopedRlzValueStoreLock lock; 500 | rlz_lib::RlzValueStore* store = lock.GetStore(); 501 | if (!store || !store->HasAccess(rlz_lib::RlzValueStore::kWriteAccess)) 502 | return false; 503 | 504 | std::string response_string(response); 505 | int response_length = -1; 506 | if (!IsPingResponseValid(response, &response_length)) 507 | return false; 508 | 509 | if (0 == response_length) 510 | return true; // Empty response - no parsing. 511 | 512 | std::string events_variable; 513 | std::string stateful_events_variable; 514 | base::SStringPrintf(&events_variable, "%s: ", kEventsCgiVariable); 515 | base::SStringPrintf(&stateful_events_variable, "%s: ", 516 | kStatefulEventsCgiVariable); 517 | 518 | int rlz_cgi_length = strlen(kRlzCgiVariable); 519 | 520 | // Split response lines. Expected response format is lines of the form: 521 | // rlzW1: 1R1_____en__252 522 | int line_end_index = -1; 523 | do { 524 | int line_begin = line_end_index + 1; 525 | line_end_index = response_string.find("\n", line_begin); 526 | 527 | int line_end = line_end_index; 528 | if (line_end < 0) 529 | line_end = response_length; 530 | 531 | if (line_end <= line_begin) 532 | continue; // Empty line. 533 | 534 | std::string response_line; 535 | response_line = response_string.substr(line_begin, line_end - line_begin); 536 | 537 | if (StartsWithASCII(response_line, kRlzCgiVariable, true)) { // An RLZ. 538 | int separator_index = -1; 539 | if ((separator_index = response_line.find(": ")) < 0) 540 | continue; // Not a valid key-value pair. 541 | 542 | // Get the access point. 543 | std::string point_name = 544 | response_line.substr(3, separator_index - rlz_cgi_length); 545 | AccessPoint point = NO_ACCESS_POINT; 546 | if (!GetAccessPointFromName(point_name.c_str(), &point) || 547 | point == NO_ACCESS_POINT) 548 | continue; // Not a valid access point. 549 | 550 | // Get the new RLZ. 551 | std::string rlz_value(response_line.substr(separator_index + 2)); 552 | TrimWhitespaceASCII(rlz_value, TRIM_LEADING, &rlz_value); 553 | 554 | int rlz_length = rlz_value.find_first_of("\r\n "); 555 | if (rlz_length < 0) 556 | rlz_length = rlz_value.size(); 557 | 558 | if (rlz_length > kMaxRlzLength) 559 | continue; // Too long. 560 | 561 | if (IsAccessPointSupported(point)) 562 | SetAccessPointRlz(point, rlz_value.substr(0, rlz_length).c_str()); 563 | } else if (StartsWithASCII(response_line, events_variable, true)) { 564 | // Clear events which server parsed. 565 | std::vector event_array; 566 | GetEventsFromResponseString(response_line, events_variable, &event_array); 567 | for (size_t i = 0; i < event_array.size(); ++i) { 568 | ClearProductEvent(product, event_array[i].access_point, 569 | event_array[i].event_type); 570 | } 571 | } else if (StartsWithASCII(response_line, stateful_events_variable, true)) { 572 | // Record any stateful events the server send over. 573 | std::vector event_array; 574 | GetEventsFromResponseString(response_line, stateful_events_variable, 575 | &event_array); 576 | for (size_t i = 0; i < event_array.size(); ++i) { 577 | RecordStatefulEvent(product, event_array[i].access_point, 578 | event_array[i].event_type); 579 | } 580 | } 581 | } while (line_end_index >= 0); 582 | 583 | #if defined(OS_WIN) 584 | // Update the DCC in registry if needed. 585 | SetMachineDealCodeFromPingResponse(response); 586 | #endif 587 | 588 | return true; 589 | } 590 | 591 | bool GetPingParams(Product product, const AccessPoint* access_points, 592 | char* cgi, size_t cgi_size) { 593 | if (!cgi || cgi_size <= 0) { 594 | ASSERT_STRING("GetPingParams: Invalid buffer"); 595 | return false; 596 | } 597 | 598 | cgi[0] = 0; 599 | 600 | if (!access_points) { 601 | ASSERT_STRING("GetPingParams: access_points is NULL"); 602 | return false; 603 | } 604 | 605 | // Add the RLZ Exchange Protocol version. 606 | std::string cgi_string(kProtocolCgiArgument); 607 | 608 | // Copy the &rlz= over. 609 | base::StringAppendF(&cgi_string, "&%s=", kRlzCgiVariable); 610 | 611 | { 612 | // Now add each of the RLZ's. Keep the lock during all GetAccessPointRlz() 613 | // calls below. 614 | ScopedRlzValueStoreLock lock; 615 | RlzValueStore* store = lock.GetStore(); 616 | if (!store || !store->HasAccess(RlzValueStore::kReadAccess)) 617 | return false; 618 | bool first_rlz = true; // comma before every RLZ but the first. 619 | for (int i = 0; access_points[i] != NO_ACCESS_POINT; i++) { 620 | char rlz[kMaxRlzLength + 1]; 621 | if (GetAccessPointRlz(access_points[i], rlz, arraysize(rlz))) { 622 | const char* access_point = GetAccessPointName(access_points[i]); 623 | if (!access_point) 624 | continue; 625 | 626 | base::StringAppendF(&cgi_string, "%s%s%s%s", 627 | first_rlz ? "" : kRlzCgiSeparator, 628 | access_point, kRlzCgiIndicator, rlz); 629 | first_rlz = false; 630 | } 631 | } 632 | 633 | #if defined(OS_WIN) 634 | // Report the DCC too if not empty. DCCs are windows-only. 635 | char dcc[kMaxDccLength + 1]; 636 | dcc[0] = 0; 637 | if (GetMachineDealCode(dcc, arraysize(dcc)) && dcc[0]) 638 | base::StringAppendF(&cgi_string, "&%s=%s", kDccCgiVariable, dcc); 639 | #endif 640 | } 641 | 642 | if (cgi_string.size() >= cgi_size) 643 | return false; 644 | 645 | strncpy(cgi, cgi_string.c_str(), cgi_size); 646 | cgi[cgi_size - 1] = 0; 647 | 648 | return true; 649 | } 650 | 651 | } // namespace rlz_lib 652 | -------------------------------------------------------------------------------- /lib/rlz_lib.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // A library to manage RLZ information for access-points shared 6 | // across different client applications. 7 | // 8 | // All functions return true on success and false on error. 9 | // This implemenation is thread safe. 10 | 11 | 12 | #ifndef RLZ_LIB_RLZ_LIB_H_ 13 | #define RLZ_LIB_RLZ_LIB_H_ 14 | 15 | #include 16 | #include 17 | 18 | #include "build/build_config.h" 19 | 20 | #include "rlz/lib/rlz_enums.h" 21 | 22 | #if defined(OS_WIN) 23 | #define RLZ_LIB_API __cdecl 24 | #else 25 | #define RLZ_LIB_API 26 | #endif 27 | 28 | // Define one of 29 | // + RLZ_NETWORK_IMPLEMENTATION_WIN_INET: Uses win inet to send financial pings. 30 | // + RLZ_NETWORK_IMPLEMENTATION_CHROME_NET: Uses chrome's network stack to send 31 | // financial pings. rlz_lib::SetURLRequestContext() must be called before 32 | // any calls to SendFinancialPing(). 33 | #if defined(RLZ_NETWORK_IMPLEMENTATION_WIN_INET) && \ 34 | defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET) 35 | #error Exactly one of RLZ_NETWORK_IMPLEMENTATION_WIN_INET and \ 36 | RLZ_NETWORK_IMPLEMENTATION_CHROME_NET should be defined. 37 | #endif 38 | #if !defined(RLZ_NETWORK_IMPLEMENTATION_WIN_INET) && \ 39 | !defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET) 40 | #if defined(OS_WIN) 41 | #define RLZ_NETWORK_IMPLEMENTATION_WIN_INET 42 | #else 43 | #define RLZ_NETWORK_IMPLEMENTATION_CHROME_NET 44 | #endif 45 | #endif 46 | 47 | #if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET) 48 | namespace net { 49 | class URLRequestContextGetter; 50 | } // namespace net 51 | #endif 52 | 53 | namespace rlz_lib { 54 | 55 | class ScopedRlzValueStoreLock; 56 | 57 | // The maximum length of an access points RLZ in bytes. 58 | const int kMaxRlzLength = 64; 59 | // The maximum length of an access points RLZ in bytes. 60 | const int kMaxDccLength = 128; 61 | // The maximum length of a CGI string in bytes. 62 | const int kMaxCgiLength = 2048; 63 | // The maximum length of a ping response we will parse in bytes. If the response 64 | // is bigger, please break it up into separate calls. 65 | const int kMaxPingResponseLength = 0x4000; // 16K 66 | 67 | #if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET) 68 | // Set the URLRequestContextGetter used by SendFinancialPing(). The IO message 69 | // loop returned by this context will be used for the IO done by 70 | // SendFinancialPing(). 71 | bool RLZ_LIB_API SetURLRequestContext(net::URLRequestContextGetter* context); 72 | #endif 73 | 74 | // RLZ storage functions. 75 | 76 | // Get all the events reported by this product as a CGI string to append to 77 | // the daily ping. 78 | // Access: HKCU read. 79 | bool RLZ_LIB_API GetProductEventsAsCgi(Product product, char* unescaped_cgi, 80 | size_t unescaped_cgi_size); 81 | 82 | // Records an RLZ event. 83 | // Some events can be product-independent (e.g: First search from home page), 84 | // and some can be access point independent (e.g. Pack installed). However, 85 | // product independent events must still include the product which cares about 86 | // that information being reported. 87 | // Access: HKCU write. 88 | bool RLZ_LIB_API RecordProductEvent(Product product, AccessPoint point, 89 | Event event_id); 90 | 91 | // Clear an event reported by this product. This should be called after a 92 | // successful ping to the RLZ server. 93 | // Access: HKCU write. 94 | bool RLZ_LIB_API ClearProductEvent(Product product, AccessPoint point, 95 | Event event_id); 96 | 97 | // Clear all reported events and recorded stateful events of this product. 98 | // This should be called on complete uninstallation of the product. 99 | // Access: HKCU write. 100 | bool RLZ_LIB_API ClearAllProductEvents(Product product); 101 | 102 | // Clears all product-specifc state from the RLZ registry. 103 | // Should be called during product uninstallation. 104 | // This removes outstanding product events, product financial ping times, 105 | // the product RLS argument (if any), and any RLZ's for access points being 106 | // uninstalled with the product. 107 | // access_points is an array terminated with NO_ACCESS_POINT. 108 | // IMPORTANT: These are the access_points the product is removing as part 109 | // of the uninstallation, not necessarily all the access points passed to 110 | // SendFinancialPing() and GetPingParams(). 111 | // access_points can be NULL if no points are being uninstalled. 112 | // No return value - this is best effort. Will assert in debug mode on 113 | // failed attempts. 114 | // Access: HKCU write. 115 | void RLZ_LIB_API ClearProductState(Product product, 116 | const AccessPoint* access_points); 117 | 118 | // Get the RLZ value of the access point. If the access point is not Google, the 119 | // RLZ will be the empty string and the function will return false. 120 | // Access: HKCU read. 121 | bool RLZ_LIB_API GetAccessPointRlz(AccessPoint point, char* rlz, 122 | size_t rlz_size); 123 | 124 | // Set the RLZ for the access-point. Fails and asserts if called when the access 125 | // point is not set to Google. 126 | // new_rlz should come from a server-response. Client applications should not 127 | // create their own RLZ values. 128 | // Access: HKCU write. 129 | bool RLZ_LIB_API SetAccessPointRlz(AccessPoint point, const char* new_rlz); 130 | 131 | // Financial Server pinging functions. 132 | // These functions deal with pinging the RLZ financial server and parsing and 133 | // acting upon the response. Clients should SendFinancialPing() to avoid needing 134 | // these functions. However, these functions allow clients to split the various 135 | // parts of the pinging process up as needed (to avoid firewalls, etc). 136 | 137 | // Forms the HTTP request to send to the RLZ financial server. 138 | // 139 | // product : The product to ping for. 140 | // access_points : The access points this product affects. Array must be 141 | // terminated with NO_ACCESS_POINT. 142 | // product_signature : The signature sent with daily pings (e.g. swg, ietb) 143 | // product_brand : The brand of the pinging product, if any. 144 | // product_id : The product-specific installation ID (can be NULL). 145 | // product_lang : The language for the product (used to determine cohort). 146 | // exclude_machine_id : Whether the Machine ID should be explicitly excluded 147 | // based on the products privacy policy. 148 | // request : The buffer where the function returns the HTTP request. 149 | // request_buffer_size: The size of the request buffer in bytes. The buffer 150 | // size (kMaxCgiLength+1) is guaranteed to be enough. 151 | // 152 | // Access: HKCU read. 153 | bool RLZ_LIB_API FormFinancialPingRequest(Product product, 154 | const AccessPoint* access_points, 155 | const char* product_signature, 156 | const char* product_brand, 157 | const char* product_id, 158 | const char* product_lang, 159 | bool exclude_machine_id, 160 | char* request, 161 | size_t request_buffer_size); 162 | 163 | // Pings the financial server and returns the HTTP response. This will fail 164 | // if it is too early to ping the server since the last ping. 165 | // 166 | // If RLZ_NETWORK_IMPLEMENTATION_CHROME_NET is set, SetURLRequestContext() needs 167 | // to be called before calling this function. 168 | // 169 | // product : The product to ping for. 170 | // request : The HTTP request (for example, returned by 171 | // FormFinancialPingRequest). 172 | // response : The buffer in which the HTTP response is returned. 173 | // response_buffer_size : The size of the response buffer in bytes. The buffer 174 | // size (kMaxPingResponseLength+1) is enough for all 175 | // legitimate server responses (any response that is 176 | // bigger should be considered the same way as a general 177 | // network problem). 178 | // 179 | // Access: HKCU read. 180 | bool RLZ_LIB_API PingFinancialServer(Product product, 181 | const char* request, 182 | char* response, 183 | size_t response_buffer_size); 184 | 185 | // Checks if a ping response is valid - ie. it has a checksum line which 186 | // is the CRC-32 checksum of the message uptil the checksum. If 187 | // checksum_idx is not NULL, it will get the index of the checksum, i.e. - 188 | // the effective end of the message. 189 | // Access: No restrictions. 190 | bool RLZ_LIB_API IsPingResponseValid(const char* response, 191 | int* checksum_idx); 192 | 193 | 194 | // Complex helpers built on top of other functions. 195 | 196 | // Parses the responses from the financial server and updates product state 197 | // and access point RLZ's in registry. Like ParsePingResponse(), but also 198 | // updates the last ping time. 199 | // Access: HKCU write. 200 | bool RLZ_LIB_API ParseFinancialPingResponse(Product product, 201 | const char* response); 202 | 203 | // Send the ping with RLZs and events to the PSO server. 204 | // This ping method should be called daily. (More frequent calls will fail). 205 | // Also, if there are no events, the call will succeed only once a week. 206 | // 207 | // If RLZ_NETWORK_IMPLEMENTATION_CHROME_NET is set, SetURLRequestContext() needs 208 | // to be called before calling this function. 209 | // 210 | // product : The product to ping for. 211 | // access_points : The access points this product affects. Array must be 212 | // terminated with NO_ACCESS_POINT. 213 | // product_signature : The signature sent with daily pings (e.g. swg, ietb) 214 | // product_brand : The brand of the pinging product, if any. 215 | // product_id : The product-specific installation ID (can be NULL). 216 | // product_lang : The language for the product (used to determine cohort). 217 | // exclude_machine_id : Whether the Machine ID should be explicitly excluded 218 | // based on the products privacy policy. 219 | // 220 | // Returns true on successful ping and response, false otherwise. 221 | // Access: HKCU write. 222 | bool RLZ_LIB_API SendFinancialPing(Product product, 223 | const AccessPoint* access_points, 224 | const char* product_signature, 225 | const char* product_brand, 226 | const char* product_id, 227 | const char* product_lang, 228 | bool exclude_machine_id); 229 | 230 | // An alternate implementations of SendFinancialPing with the same behavior, 231 | // except the caller can optionally choose to skip the timing check. 232 | bool RLZ_LIB_API SendFinancialPing(Product product, 233 | const AccessPoint* access_points, 234 | const char* product_signature, 235 | const char* product_brand, 236 | const char* product_id, 237 | const char* product_lang, 238 | bool exclude_machine_id, 239 | const bool skip_time_check); 240 | 241 | // Parses RLZ related ping response information from the server. 242 | // Updates stored RLZ values and clears stored events accordingly. 243 | // Access: HKCU write. 244 | bool RLZ_LIB_API ParsePingResponse(Product product, const char* response); 245 | 246 | 247 | // Copies the events associated with the product and the RLZ's for each access 248 | // point in access_points into cgi. This string can be directly appended 249 | // to a ping (will need an & if not first paramter). 250 | // access_points must be an array of AccessPoints terminated with 251 | // NO_ACCESS_POINT. 252 | // Access: HKCU read. 253 | bool RLZ_LIB_API GetPingParams(Product product, 254 | const AccessPoint* access_points, 255 | char* unescaped_cgi, size_t unescaped_cgi_size); 256 | 257 | #if defined(OS_WIN) 258 | // OEM Deal confirmation storage functions. OEM Deals are windows-only. 259 | 260 | // Makes the OEM Deal Confirmation code writable by all users on the machine. 261 | // This should be called before calling SetMachineDealCode from a non-admin 262 | // account. 263 | // Access: HKLM write. 264 | bool RLZ_LIB_API CreateMachineState(void); 265 | 266 | // Set the OEM Deal Confirmation Code (DCC). This information is used for RLZ 267 | // initalization. 268 | // Access: HKLM write, or 269 | // HKCU read if rlz_lib::CreateMachineState() has been sucessfully called. 270 | bool RLZ_LIB_API SetMachineDealCode(const char* dcc); 271 | 272 | // Get the DCC cgi argument string to append to a daily ping. 273 | // Should be used only by OEM deal trackers. Applications should use the 274 | // GetMachineDealCode method which has an AccessPoint paramter. 275 | // Access: HKLM read. 276 | bool RLZ_LIB_API GetMachineDealCodeAsCgi(char* cgi, size_t cgi_size); 277 | 278 | // Get the DCC value stored in registry. 279 | // Should be used only by OEM deal trackers. Applications should use the 280 | // GetMachineDealCode method which has an AccessPoint paramter. 281 | // Access: HKLM read. 282 | bool RLZ_LIB_API GetMachineDealCode(char* dcc, size_t dcc_size); 283 | 284 | // Parses a ping response, checks if it is valid and sets the machine DCC 285 | // from the response. The ping must also contain the current DCC value in 286 | // order to be considered valid. 287 | // Access: HKLM write; 288 | // HKCU write if CreateMachineState() has been successfully called. 289 | bool RLZ_LIB_API SetMachineDealCodeFromPingResponse(const char* response); 290 | 291 | #endif 292 | 293 | // Segment RLZ persistence based on branding information. 294 | // All information for a given product is persisted under keys with the either 295 | // product's name or its access point's name. This assumes that only 296 | // one instance of the product is installed on the machine, and that only one 297 | // product brand is associated with it. 298 | // 299 | // In some cases, a given product may be using supplementary brands. The RLZ 300 | // information must be kept separately for each of these brands. To achieve 301 | // this segmentation, scope all RLZ library calls that deal with supplementary 302 | // brands within the lifetime of an rlz_lib::ProductBranding instance. 303 | // 304 | // For example, to record events for a supplementary brand, do the following: 305 | // 306 | // { 307 | // rlz_lib::SupplementaryBranding branding("AAAA"); 308 | // // This call to RecordProductEvent is scoped to the AAAA brand. 309 | // rlz_lib::RecordProductEvent(rlz_lib::DESKTOP, rlz_lib::GD_DESKBAND, 310 | // rlz_lib::INSTALL); 311 | // } 312 | // 313 | // // This call to RecordProductEvent is not scoped to any supplementary brand. 314 | // rlz_lib::RecordProductEvent(rlz_lib::DESKTOP, rlz_lib::GD_DESKBAND, 315 | // rlz_lib::INSTALL); 316 | // 317 | // In particular, this affects the recording of stateful events and the sending 318 | // of financial pings. In the former case, a stateful event recorded while 319 | // scoped to a supplementary brand will be recorded again when scoped to a 320 | // different supplementary brand (or not scoped at all). In the latter case, 321 | // the time skip check is specific to each supplementary brand. 322 | class SupplementaryBranding { 323 | public: 324 | SupplementaryBranding(const char* brand); 325 | ~SupplementaryBranding(); 326 | 327 | static const std::string& GetBrand(); 328 | 329 | private: 330 | ScopedRlzValueStoreLock* lock_; 331 | }; 332 | 333 | } // namespace rlz_lib 334 | 335 | #endif // RLZ_LIB_RLZ_LIB_H_ 336 | -------------------------------------------------------------------------------- /lib/rlz_lib_clear.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | 5 | // The methods in this file belong conceptually to rlz_lib.cc. However, some 6 | // programs depend on rlz only to call ClearAllProductEvents(), so this file 7 | // contains this in fairly self-contained form to make it easier for linkers 8 | // to strip away most of rlz. In particular, this file should not reference any 9 | // symbols defined in financial_ping.cc. 10 | 11 | #include "rlz/lib/rlz_lib.h" 12 | 13 | #include "base/lazy_instance.h" 14 | #include "rlz/lib/assert.h" 15 | #include "rlz/lib/rlz_value_store.h" 16 | 17 | namespace rlz_lib { 18 | 19 | bool ClearAllProductEvents(Product product) { 20 | rlz_lib::ScopedRlzValueStoreLock lock; 21 | rlz_lib::RlzValueStore* store = lock.GetStore(); 22 | if (!store || !store->HasAccess(rlz_lib::RlzValueStore::kWriteAccess)) 23 | return false; 24 | 25 | bool result; 26 | result = store->ClearAllProductEvents(product); 27 | result &= store->ClearAllStatefulEvents(product); 28 | return result; 29 | } 30 | 31 | void ClearProductState(Product product, const AccessPoint* access_points) { 32 | rlz_lib::ScopedRlzValueStoreLock lock; 33 | rlz_lib::RlzValueStore* store = lock.GetStore(); 34 | if (!store || !store->HasAccess(rlz_lib::RlzValueStore::kWriteAccess)) 35 | return; 36 | 37 | // Delete all product specific state. 38 | VERIFY(ClearAllProductEvents(product)); 39 | VERIFY(store->ClearPingTime(product)); 40 | 41 | // Delete all RLZ's for access points being uninstalled. 42 | if (access_points) { 43 | for (int i = 0; access_points[i] != NO_ACCESS_POINT; i++) { 44 | VERIFY(store->ClearAccessPointRlz(access_points[i])); 45 | } 46 | } 47 | 48 | store->CollectGarbage(); 49 | } 50 | 51 | static base::LazyInstance::Leaky g_supplemental_branding; 52 | 53 | SupplementaryBranding::SupplementaryBranding(const char* brand) 54 | : lock_(new ScopedRlzValueStoreLock) { 55 | if (!lock_->GetStore()) 56 | return; 57 | 58 | if (!g_supplemental_branding.Get().empty()) { 59 | ASSERT_STRING("ProductBranding: existing brand is not empty"); 60 | return; 61 | } 62 | 63 | if (brand == NULL || brand[0] == 0) { 64 | ASSERT_STRING("ProductBranding: new brand is empty"); 65 | return; 66 | } 67 | 68 | g_supplemental_branding.Get() = brand; 69 | } 70 | 71 | SupplementaryBranding::~SupplementaryBranding() { 72 | if (lock_->GetStore()) 73 | g_supplemental_branding.Get().clear(); 74 | delete lock_; 75 | } 76 | 77 | // static 78 | const std::string& SupplementaryBranding::GetBrand() { 79 | return g_supplemental_branding.Get(); 80 | } 81 | 82 | } // namespace rlz_lib 83 | -------------------------------------------------------------------------------- /lib/rlz_value_store.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | 5 | #ifndef RLZ_VALUE_STORE_H_ 6 | #define RLZ_VALUE_STORE_H_ 7 | 8 | #include "base/basictypes.h" 9 | #include "base/memory/scoped_ptr.h" 10 | #include "rlz/lib/rlz_enums.h" 11 | 12 | #if defined(OS_WIN) 13 | #include "rlz/win/lib/lib_mutex.h" 14 | #endif 15 | 16 | #if defined(OS_MACOSX) 17 | #include "base/mac/scoped_nsautorelease_pool.h" 18 | #endif 19 | 20 | 21 | #include 22 | #include 23 | 24 | class FilePath; 25 | 26 | namespace rlz_lib { 27 | 28 | // Abstracts away rlz's key value store. On windows, this usually writes to 29 | // the registry. On mac, it writes to an NSDefaults object. 30 | class RlzValueStore { 31 | public: 32 | virtual ~RlzValueStore() {} 33 | 34 | enum AccessType { kReadAccess, kWriteAccess }; 35 | virtual bool HasAccess(AccessType type) = 0; 36 | 37 | // Ping times. 38 | virtual bool WritePingTime(Product product, int64 time) = 0; 39 | virtual bool ReadPingTime(Product product, int64* time) = 0; 40 | virtual bool ClearPingTime(Product product) = 0; 41 | 42 | // Access point RLZs. 43 | virtual bool WriteAccessPointRlz(AccessPoint access_point, 44 | const char* new_rlz) = 0; 45 | virtual bool ReadAccessPointRlz(AccessPoint access_point, 46 | char* rlz, // At most kMaxRlzLength + 1 bytes 47 | size_t rlz_size) = 0; 48 | virtual bool ClearAccessPointRlz(AccessPoint access_point) = 0; 49 | 50 | // Product events. 51 | // Stores |event_rlz| for product |product| as product event. 52 | virtual bool AddProductEvent(Product product, const char* event_rlz) = 0; 53 | // Appends all events for |product| to |events|, in arbirtrary order. 54 | virtual bool ReadProductEvents(Product product, 55 | std::vector* events) = 0; 56 | // Removes the stored event |event_rlz| for |product| if it exists. 57 | virtual bool ClearProductEvent(Product product, const char* event_rlz) = 0; 58 | // Removes all stored product events for |product|. 59 | virtual bool ClearAllProductEvents(Product product) = 0; 60 | 61 | // Stateful events. 62 | // Stores |event_rlz| for product |product| as stateful event. 63 | virtual bool AddStatefulEvent(Product product, const char* event_rlz) = 0; 64 | // Checks if |event_rlz| has been stored as stateful event for |product|. 65 | virtual bool IsStatefulEvent(Product product, const char* event_rlz) = 0; 66 | // Removes all stored stateful events for |product|. 67 | virtual bool ClearAllStatefulEvents(Product product) = 0; 68 | 69 | // Tells the value store to clean up unimportant internal data structures, for 70 | // example empty registry folders, that might remain after clearing other 71 | // data. Best-effort. 72 | virtual void CollectGarbage() = 0; 73 | }; 74 | 75 | // All methods of RlzValueStore must stays consistent even when accessed from 76 | // multiple threads in multiple processes. To enforce this through the type 77 | // system, the only way to access the RlzValueStore is through a 78 | // ScopedRlzValueStoreLock, which is a cross-process lock. It is active while 79 | // it is in scope. If the class fails to acquire a lock, its GetStore() method 80 | // returns NULL. If the lock fails to be acquired, it must not be taken 81 | // recursively. That is, all user code should look like this: 82 | // ScopedRlzValueStoreLock lock; 83 | // RlzValueStore* store = lock.GetStore(); 84 | // if (!store) 85 | // return some_error_code; 86 | // ... 87 | class ScopedRlzValueStoreLock { 88 | public: 89 | ScopedRlzValueStoreLock(); 90 | ~ScopedRlzValueStoreLock(); 91 | 92 | // Returns a RlzValueStore protected by a cross-process lock, or NULL if the 93 | // lock can't be obtained. The lifetime of the returned object is limited to 94 | // the lifetime of this ScopedRlzValueStoreLock object. 95 | RlzValueStore* GetStore(); 96 | 97 | private: 98 | scoped_ptr store_; 99 | #if defined(OS_WIN) 100 | LibMutex lock_; 101 | #else 102 | base::mac::ScopedNSAutoreleasePool autorelease_pool_; 103 | #endif 104 | }; 105 | 106 | #if defined(OS_MACOSX) 107 | namespace testing { 108 | // Prefix |directory| to the path where the RLZ data file lives, for tests. 109 | void SetRlzStoreDirectory(const FilePath& directory); 110 | } // namespace testing 111 | #endif // defined(OS_MACOSX) 112 | 113 | 114 | } // namespace rlz_lib 115 | 116 | #endif // RLZ_VALUE_STORE_H_ 117 | -------------------------------------------------------------------------------- /lib/string_utils.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // String manipulation functions used in the RLZ library. 6 | 7 | #include "rlz/lib/string_utils.h" 8 | 9 | #include "rlz/lib/assert.h" 10 | 11 | namespace rlz_lib { 12 | 13 | bool IsAscii(char letter) { 14 | return (letter >= 0x0 && letter < 0x80); 15 | } 16 | 17 | bool GetHexValue(char letter, int* value) { 18 | if (!value) { 19 | ASSERT_STRING("GetHexValue: Invalid output paramter"); 20 | return false; 21 | } 22 | *value = 0; 23 | 24 | if (letter >= '0' && letter <= '9') 25 | *value = letter - '0'; 26 | else if (letter >= 'a' && letter <= 'f') 27 | *value = (letter - 'a') + 0xA; 28 | else if (letter >= 'A' && letter <= 'F') 29 | *value = (letter - 'A') + 0xA; 30 | else 31 | return false; 32 | 33 | return true; 34 | } 35 | 36 | int HexStringToInteger(const char* text) { 37 | if (!text) { 38 | ASSERT_STRING("HexStringToInteger: text is NULL."); 39 | return 0; 40 | } 41 | 42 | int idx = 0; 43 | // Ignore leading whitespace. 44 | while (text[idx] == '\t' || text[idx] == ' ') 45 | idx++; 46 | 47 | if ((text[idx] == '0') && 48 | (text[idx + 1] == 'X' || text[idx + 1] == 'x')) 49 | idx +=2; // String is of the form 0x... 50 | 51 | int number = 0; 52 | int digit = 0; 53 | for (; text[idx] != '\0'; idx++) { 54 | if (!GetHexValue(text[idx], &digit)) { 55 | // Ignore trailing whitespaces, but assert on other trailing characters. 56 | bool only_whitespaces = true; 57 | while (only_whitespaces && text[idx]) 58 | only_whitespaces = (text[idx++] == ' '); 59 | if (!only_whitespaces) 60 | ASSERT_STRING("HexStringToInteger: text contains non-hex characters."); 61 | return number; 62 | } 63 | number = (number << 4) | digit; 64 | } 65 | 66 | return number; 67 | } 68 | 69 | bool BytesToString(const unsigned char* data, 70 | int data_len, 71 | std::string* string) { 72 | if (!string) 73 | return false; 74 | 75 | string->clear(); 76 | if (data_len < 1 || !data) 77 | return false; 78 | 79 | static const char kHex[] = "0123456789ABCDEF"; 80 | 81 | // Fix the buffer size to begin with to avoid repeated re-allocation. 82 | string->resize(data_len * 2); 83 | int index = data_len; 84 | while (index--) { 85 | string->at(2 * index) = kHex[data[index] >> 4]; // high digit 86 | string->at(2 * index + 1) = kHex[data[index] & 0x0F]; // low digit 87 | } 88 | 89 | return true; 90 | } 91 | 92 | } // namespace rlz_lib 93 | -------------------------------------------------------------------------------- /lib/string_utils.h: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // String manipulation functions used in the RLZ library. 6 | 7 | #ifndef RLZ_LIB_STRING_UTILS_H_ 8 | #define RLZ_LIB_STRING_UTILS_H_ 9 | 10 | #include 11 | 12 | namespace rlz_lib { 13 | 14 | bool IsAscii(char letter); 15 | 16 | bool BytesToString(const unsigned char* data, 17 | int data_len, 18 | std::string* string); 19 | 20 | bool GetHexValue(char letter, int* value); 21 | 22 | int HexStringToInteger(const char* text); 23 | 24 | }; // namespace 25 | 26 | #endif // RLZ_LIB_STRING_UTILS_H_ 27 | -------------------------------------------------------------------------------- /lib/string_utils_unittest.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // Unit test for string manipulation functions used in the RLZ library. 6 | 7 | #include "rlz/lib/string_utils.h" 8 | 9 | #include "base/basictypes.h" 10 | #include "base/logging.h" 11 | #include "base/utf_string_conversions.h" 12 | #include "rlz/lib/assert.h" 13 | #include "testing/gmock/include/gmock/gmock.h" 14 | #include "testing/gtest/include/gtest/gtest.h" 15 | 16 | TEST(StringUtilsUnittest, IsAscii) { 17 | rlz_lib::SetExpectedAssertion(""); 18 | 19 | char bad_letters[] = {'\x80', '\xA0', '\xFF'}; 20 | for (size_t i = 0; i < arraysize(bad_letters); ++i) 21 | EXPECT_FALSE(rlz_lib::IsAscii(bad_letters[i])); 22 | 23 | char good_letters[] = {'A', '~', '\n', 0x7F, 0x00}; 24 | for (size_t i = 0; i < arraysize(good_letters); ++i) 25 | EXPECT_TRUE(rlz_lib::IsAscii(good_letters[i])); 26 | } 27 | 28 | TEST(StringUtilsUnittest, HexStringToInteger) { 29 | rlz_lib::SetExpectedAssertion("HexStringToInteger: text is NULL."); 30 | EXPECT_EQ(0, rlz_lib::HexStringToInteger(NULL)); 31 | 32 | rlz_lib::SetExpectedAssertion(""); 33 | EXPECT_EQ(0, rlz_lib::HexStringToInteger("")); 34 | EXPECT_EQ(0, rlz_lib::HexStringToInteger(" ")); 35 | EXPECT_EQ(0, rlz_lib::HexStringToInteger(" 0x ")); 36 | EXPECT_EQ(0, rlz_lib::HexStringToInteger(" 0x0 ")); 37 | EXPECT_EQ(0x12345, rlz_lib::HexStringToInteger("12345")); 38 | EXPECT_EQ(0xa34Ed0, rlz_lib::HexStringToInteger("a34Ed0")); 39 | EXPECT_EQ(0xa34Ed0, rlz_lib::HexStringToInteger("0xa34Ed0")); 40 | EXPECT_EQ(0xa34Ed0, rlz_lib::HexStringToInteger(" 0xa34Ed0")); 41 | EXPECT_EQ(0xa34Ed0, rlz_lib::HexStringToInteger("0xa34Ed0 ")); 42 | EXPECT_EQ(0xa34Ed0, rlz_lib::HexStringToInteger(" 0xa34Ed0 ")); 43 | EXPECT_EQ(0xa34Ed0, rlz_lib::HexStringToInteger(" 0x000a34Ed0 ")); 44 | EXPECT_EQ(0xa34Ed0, rlz_lib::HexStringToInteger(" 000a34Ed0 ")); 45 | 46 | rlz_lib::SetExpectedAssertion( 47 | "HexStringToInteger: text contains non-hex characters."); 48 | EXPECT_EQ(0x12ff, rlz_lib::HexStringToInteger("12ffg")); 49 | EXPECT_EQ(0x12f, rlz_lib::HexStringToInteger("12f 121")); 50 | EXPECT_EQ(0x12f, rlz_lib::HexStringToInteger("12f 121")); 51 | EXPECT_EQ(0, rlz_lib::HexStringToInteger("g12f")); 52 | EXPECT_EQ(0, rlz_lib::HexStringToInteger(" 0x0 \n")); 53 | 54 | rlz_lib::SetExpectedAssertion(""); 55 | } 56 | 57 | TEST(StringUtilsUnittest, TestBytesToString) { 58 | unsigned char data[] = {0x1E, 0x00, 0x21, 0x67, 0xFF}; 59 | std::string result; 60 | 61 | EXPECT_FALSE(rlz_lib::BytesToString(NULL, 5, &result)); 62 | EXPECT_FALSE(rlz_lib::BytesToString(data, 5, NULL)); 63 | EXPECT_FALSE(rlz_lib::BytesToString(NULL, 5, NULL)); 64 | 65 | EXPECT_TRUE(rlz_lib::BytesToString(data, 5, &result)); 66 | EXPECT_EQ(std::string("1E002167FF"), result); 67 | EXPECT_TRUE(rlz_lib::BytesToString(data, 4, &result)); 68 | EXPECT_EQ(std::string("1E002167"), result); 69 | } 70 | -------------------------------------------------------------------------------- /mac/lib/machine_id_mac.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "base/logging.h" 12 | #include "base/mac/foundation_util.h" 13 | #include "base/mac/scoped_cftyperef.h" 14 | #include "base/mac/scoped_ioobject.h" 15 | #include "base/string16.h" 16 | #include "base/stringprintf.h" 17 | #include "base/sys_string_conversions.h" 18 | #include "base/utf_string_conversions.h" 19 | 20 | namespace rlz_lib { 21 | 22 | namespace { 23 | 24 | // See http://developer.apple.com/library/mac/#technotes/tn1103/_index.html 25 | 26 | // The caller is responsible for freeing |matching_services|. 27 | bool FindEthernetInterfaces(io_iterator_t* matching_services) { 28 | base::mac::ScopedCFTypeRef matching_dict( 29 | IOServiceMatching(kIOEthernetInterfaceClass)); 30 | if (!matching_dict) 31 | return false; 32 | 33 | base::mac::ScopedCFTypeRef primary_interface( 34 | CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 35 | &kCFTypeDictionaryKeyCallBacks, 36 | &kCFTypeDictionaryValueCallBacks)); 37 | if (!primary_interface) 38 | return false; 39 | 40 | CFDictionarySetValue( 41 | primary_interface, CFSTR(kIOPrimaryInterface), kCFBooleanTrue); 42 | CFDictionarySetValue( 43 | matching_dict, CFSTR(kIOPropertyMatchKey), primary_interface); 44 | 45 | kern_return_t kern_result = IOServiceGetMatchingServices( 46 | kIOMasterPortDefault, matching_dict.release(), matching_services); 47 | 48 | return kern_result == KERN_SUCCESS; 49 | } 50 | 51 | bool GetMACAddressFromIterator(io_iterator_t primary_interface_iterator, 52 | uint8_t* buffer, size_t buffer_size) { 53 | if (buffer_size < kIOEthernetAddressSize) 54 | return false; 55 | 56 | bool success = false; 57 | 58 | bzero(buffer, buffer_size); 59 | base::mac::ScopedIOObject primary_interface; 60 | while (primary_interface.reset(IOIteratorNext(primary_interface_iterator)), 61 | primary_interface) { 62 | io_object_t primary_interface_parent; 63 | kern_return_t kern_result = IORegistryEntryGetParentEntry( 64 | primary_interface, kIOServicePlane, &primary_interface_parent); 65 | base::mac::ScopedIOObject primary_interface_parent_deleter( 66 | primary_interface_parent); 67 | success = kern_result == KERN_SUCCESS; 68 | 69 | if (!success) 70 | continue; 71 | 72 | base::mac::ScopedCFTypeRef mac_data( 73 | IORegistryEntryCreateCFProperty(primary_interface_parent, 74 | CFSTR(kIOMACAddress), 75 | kCFAllocatorDefault, 76 | 0)); 77 | CFDataRef mac_data_data = base::mac::CFCast(mac_data); 78 | if (mac_data_data) { 79 | CFDataGetBytes( 80 | mac_data_data, CFRangeMake(0, kIOEthernetAddressSize), buffer); 81 | } 82 | } 83 | 84 | return success; 85 | } 86 | 87 | bool GetMacAddress(unsigned char* buffer, size_t size) { 88 | io_iterator_t primary_interface_iterator; 89 | if (!FindEthernetInterfaces(&primary_interface_iterator)) 90 | return false; 91 | bool result = GetMACAddressFromIterator( 92 | primary_interface_iterator, buffer, size); 93 | IOObjectRelease(primary_interface_iterator); 94 | return result; 95 | } 96 | 97 | CFStringRef CopySerialNumber() { 98 | base::mac::ScopedIOObject expert_device( 99 | IOServiceGetMatchingService(kIOMasterPortDefault, 100 | IOServiceMatching("IOPlatformExpertDevice"))); 101 | if (!expert_device) 102 | return NULL; 103 | 104 | base::mac::ScopedCFTypeRef serial_number( 105 | IORegistryEntryCreateCFProperty(expert_device, 106 | CFSTR(kIOPlatformSerialNumberKey), 107 | kCFAllocatorDefault, 108 | 0)); 109 | CFStringRef serial_number_cfstring = 110 | base::mac::CFCast(serial_number); 111 | if (!serial_number_cfstring) 112 | return NULL; 113 | 114 | ignore_result(serial_number.release()); 115 | return serial_number_cfstring; 116 | } 117 | 118 | } // namespace 119 | 120 | bool GetRawMachineId(string16* data, int* more_data) { 121 | uint8_t mac_address[kIOEthernetAddressSize]; 122 | 123 | data->clear(); 124 | if (GetMacAddress(mac_address, sizeof(mac_address))) { 125 | *data += ASCIIToUTF16(StringPrintf("mac:%02x%02x%02x%02x%02x%02x", 126 | mac_address[0], mac_address[1], mac_address[2], 127 | mac_address[3], mac_address[4], mac_address[5])); 128 | } 129 | 130 | // A MAC address is enough to uniquely identify a machine, but it's only 6 131 | // bytes, 3 of which are manufacturer-determined. To make brute-forcing the 132 | // SHA1 of this harder, also append the system's serial number. 133 | CFStringRef serial = CopySerialNumber(); 134 | if (serial) { 135 | if (!data->empty()) 136 | *data += UTF8ToUTF16(" "); 137 | *data += UTF8ToUTF16("serial:") + base::SysCFStringRefToUTF16(serial); 138 | CFRelease(serial); 139 | } 140 | 141 | // On windows, this is set to the volume id. Since it's not scrambled before 142 | // being sent, just set it to 1. 143 | *more_data = 1; 144 | return true; 145 | } 146 | 147 | } // namespace rlz_lib 148 | -------------------------------------------------------------------------------- /mac/lib/rlz_value_store_mac.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | 5 | #ifndef RLZ_MAC_LIB_RLZ_VALUE_STORE_MAC_H_ 6 | #define RLZ_MAC_LIB_RLZ_VALUE_STORE_MAC_H_ 7 | 8 | #include "rlz/lib/rlz_value_store.h" 9 | #include "base/compiler_specific.h" 10 | #include "base/memory/scoped_nsobject.h" 11 | 12 | @class NSDictionary; 13 | @class NSMutableDictionary; 14 | 15 | namespace rlz_lib { 16 | 17 | // An implementation of RlzValueStore for mac. It stores information in a 18 | // plist file in the user's Application Support folder. 19 | class RlzValueStoreMac : public RlzValueStore { 20 | public: 21 | virtual bool HasAccess(AccessType type) OVERRIDE; 22 | 23 | virtual bool WritePingTime(Product product, int64 time) OVERRIDE; 24 | virtual bool ReadPingTime(Product product, int64* time) OVERRIDE; 25 | virtual bool ClearPingTime(Product product) OVERRIDE; 26 | 27 | virtual bool WriteAccessPointRlz(AccessPoint access_point, 28 | const char* new_rlz) OVERRIDE; 29 | virtual bool ReadAccessPointRlz(AccessPoint access_point, 30 | char* rlz, 31 | size_t rlz_size) OVERRIDE; 32 | virtual bool ClearAccessPointRlz(AccessPoint access_point) OVERRIDE; 33 | 34 | virtual bool AddProductEvent(Product product, const char* event_rlz) OVERRIDE; 35 | virtual bool ReadProductEvents(Product product, 36 | std::vector* events) OVERRIDE; 37 | virtual bool ClearProductEvent(Product product, 38 | const char* event_rlz) OVERRIDE; 39 | virtual bool ClearAllProductEvents(Product product) OVERRIDE; 40 | 41 | virtual bool AddStatefulEvent(Product product, 42 | const char* event_rlz) OVERRIDE; 43 | virtual bool IsStatefulEvent(Product product, 44 | const char* event_rlz) OVERRIDE; 45 | virtual bool ClearAllStatefulEvents(Product product) OVERRIDE; 46 | 47 | virtual void CollectGarbage() OVERRIDE; 48 | 49 | private: 50 | // |dict| is the dictionary that backs all data. plist_path is the name of the 51 | // plist file, used solely for implementing HasAccess(). 52 | RlzValueStoreMac(NSMutableDictionary* dict, NSString* plist_path); 53 | virtual ~RlzValueStoreMac(); 54 | friend class ScopedRlzValueStoreLock; 55 | 56 | // Returns the backing dictionary that should be written to disk. 57 | NSDictionary* dictionary(); 58 | 59 | // Returns the dictionary to which all data should be written. Usually, this 60 | // is just |dictionary()|, but if supplementary branding is used, it's a 61 | // subdirectory at key "brand_". 62 | // Note that windows stores data at 63 | // rlz/name (e.g. "pingtime")/supplementalbranding/productcode 64 | // Mac on the other hand does 65 | // supplementalbranding/productcode/pingtime. 66 | NSMutableDictionary* WorkingDict(); 67 | 68 | // Returns the subdirectory of |WorkingDict()| used to store data for 69 | // product p. 70 | NSMutableDictionary* ProductDict(Product p); 71 | 72 | scoped_nsobject dict_; 73 | scoped_nsobject plist_path_; 74 | 75 | DISALLOW_COPY_AND_ASSIGN(RlzValueStoreMac); 76 | }; 77 | 78 | } // namespace rlz_lib 79 | 80 | #endif // RLZ_MAC_LIB_RLZ_VALUE_STORE_MAC_H_ 81 | -------------------------------------------------------------------------------- /mac/lib/rlz_value_store_mac.mm: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | 5 | #include "rlz/mac/lib/rlz_value_store_mac.h" 6 | 7 | #include "base/mac/foundation_util.h" 8 | #include "base/file_path.h" 9 | #include "base/logging.h" 10 | #include "base/sys_string_conversions.h" 11 | #include "rlz/lib/assert.h" 12 | #include "rlz/lib/lib_values.h" 13 | #include "rlz/lib/rlz_lib.h" 14 | 15 | #import 16 | #include 17 | 18 | using base::mac::ObjCCast; 19 | 20 | namespace rlz_lib { 21 | 22 | // These are written to disk and should not be changed. 23 | NSString* const kPingTimeKey = @"pingTime"; 24 | NSString* const kAccessPointKey = @"accessPoints"; 25 | NSString* const kProductEventKey = @"productEvents"; 26 | NSString* const kStatefulEventKey = @"statefulEvents"; 27 | 28 | namespace { 29 | 30 | NSString* GetNSProductName(Product product) { 31 | return base::SysUTF8ToNSString(GetProductName(product)); 32 | } 33 | 34 | NSString* GetNSAccessPointName(AccessPoint p) { 35 | return base::SysUTF8ToNSString(GetAccessPointName(p)); 36 | } 37 | 38 | // Retrieves a subdictionary in |p| for key |k|, creating it if necessary. 39 | // If the dictionary contains an object for |k| that is not a mutable 40 | // dictionary, that object is replaced with an empty mutable dictinary. 41 | NSMutableDictionary* GetOrCreateDict( 42 | NSMutableDictionary* p, NSString* k) { 43 | NSMutableDictionary* d = ObjCCast([p objectForKey:k]); 44 | if (!d) { 45 | d = [NSMutableDictionary dictionaryWithCapacity:0]; 46 | [p setObject:d forKey:k]; 47 | } 48 | return d; 49 | } 50 | 51 | } // namespace 52 | 53 | RlzValueStoreMac::RlzValueStoreMac(NSMutableDictionary* dict, 54 | NSString* plist_path) 55 | : dict_([dict retain]), plist_path_([plist_path retain]) { 56 | } 57 | 58 | RlzValueStoreMac::~RlzValueStoreMac() { 59 | } 60 | 61 | bool RlzValueStoreMac::HasAccess(AccessType type) { 62 | NSFileManager* manager = [NSFileManager defaultManager]; 63 | switch (type) { 64 | case kReadAccess: return [manager isReadableFileAtPath:plist_path_]; 65 | case kWriteAccess: return [manager isWritableFileAtPath:plist_path_]; 66 | } 67 | } 68 | 69 | bool RlzValueStoreMac::WritePingTime(Product product, int64 time) { 70 | NSNumber* n = [NSNumber numberWithLongLong:time]; 71 | [ProductDict(product) setObject:n forKey:kPingTimeKey]; 72 | return true; 73 | } 74 | 75 | bool RlzValueStoreMac::ReadPingTime(Product product, int64* time) { 76 | if (NSNumber* n = 77 | ObjCCast([ProductDict(product) objectForKey:kPingTimeKey])) { 78 | *time = [n longLongValue]; 79 | return true; 80 | } 81 | return false; 82 | } 83 | 84 | bool RlzValueStoreMac::ClearPingTime(Product product) { 85 | [ProductDict(product) removeObjectForKey:kPingTimeKey]; 86 | return true; 87 | } 88 | 89 | 90 | bool RlzValueStoreMac::WriteAccessPointRlz(AccessPoint access_point, 91 | const char* new_rlz) { 92 | NSMutableDictionary* d = GetOrCreateDict(WorkingDict(), kAccessPointKey); 93 | [d setObject:base::SysUTF8ToNSString(new_rlz) 94 | forKey:GetNSAccessPointName(access_point)]; 95 | return true; 96 | } 97 | 98 | bool RlzValueStoreMac::ReadAccessPointRlz(AccessPoint access_point, 99 | char* rlz, 100 | size_t rlz_size) { 101 | // Reading a non-existent access point counts as success. 102 | if (NSDictionary* d = ObjCCast( 103 | [WorkingDict() objectForKey:kAccessPointKey])) { 104 | NSString* val = ObjCCast( 105 | [d objectForKey:GetNSAccessPointName(access_point)]); 106 | if (!val) { 107 | if (rlz_size > 0) 108 | rlz[0] = '\0'; 109 | return true; 110 | } 111 | 112 | std::string s = base::SysNSStringToUTF8(val); 113 | if (s.size() >= rlz_size) { 114 | rlz[0] = 0; 115 | ASSERT_STRING("GetAccessPointRlz: Insufficient buffer size"); 116 | return false; 117 | } 118 | strncpy(rlz, s.c_str(), rlz_size); 119 | return true; 120 | } 121 | if (rlz_size > 0) 122 | rlz[0] = '\0'; 123 | return true; 124 | } 125 | 126 | bool RlzValueStoreMac::ClearAccessPointRlz(AccessPoint access_point) { 127 | if (NSMutableDictionary* d = ObjCCast( 128 | [WorkingDict() objectForKey:kAccessPointKey])) { 129 | [d removeObjectForKey:GetNSAccessPointName(access_point)]; 130 | } 131 | return true; 132 | } 133 | 134 | 135 | bool RlzValueStoreMac::AddProductEvent(Product product, 136 | const char* event_rlz) { 137 | [GetOrCreateDict(ProductDict(product), kProductEventKey) 138 | setObject:[NSNumber numberWithBool:YES] 139 | forKey:base::SysUTF8ToNSString(event_rlz)]; 140 | return true; 141 | } 142 | 143 | bool RlzValueStoreMac::ReadProductEvents(Product product, 144 | std::vector* events) { 145 | if (NSDictionary* d = ObjCCast( 146 | [ProductDict(product) objectForKey:kProductEventKey])) { 147 | for (NSString* s in d) 148 | events->push_back(base::SysNSStringToUTF8(s)); 149 | return true; 150 | } 151 | return true; 152 | } 153 | 154 | bool RlzValueStoreMac::ClearProductEvent(Product product, 155 | const char* event_rlz) { 156 | if (NSMutableDictionary* d = ObjCCast( 157 | [ProductDict(product) objectForKey:kProductEventKey])) { 158 | [d removeObjectForKey:base::SysUTF8ToNSString(event_rlz)]; 159 | return true; 160 | } 161 | return false; 162 | } 163 | 164 | bool RlzValueStoreMac::ClearAllProductEvents(Product product) { 165 | [ProductDict(product) removeObjectForKey:kProductEventKey]; 166 | return true; 167 | } 168 | 169 | 170 | bool RlzValueStoreMac::AddStatefulEvent(Product product, 171 | const char* event_rlz) { 172 | [GetOrCreateDict(ProductDict(product), kStatefulEventKey) 173 | setObject:[NSNumber numberWithBool:YES] 174 | forKey:base::SysUTF8ToNSString(event_rlz)]; 175 | return true; 176 | } 177 | 178 | bool RlzValueStoreMac::IsStatefulEvent(Product product, 179 | const char* event_rlz) { 180 | if (NSDictionary* d = ObjCCast( 181 | [ProductDict(product) objectForKey:kStatefulEventKey])) { 182 | return [d objectForKey:base::SysUTF8ToNSString(event_rlz)] != nil; 183 | } 184 | return false; 185 | } 186 | 187 | bool RlzValueStoreMac::ClearAllStatefulEvents(Product product) { 188 | [ProductDict(product) removeObjectForKey:kStatefulEventKey]; 189 | return true; 190 | } 191 | 192 | 193 | void RlzValueStoreMac::CollectGarbage() { 194 | NOTIMPLEMENTED(); 195 | } 196 | 197 | NSDictionary* RlzValueStoreMac::dictionary() { 198 | return dict_.get(); 199 | } 200 | 201 | NSMutableDictionary* RlzValueStoreMac::WorkingDict() { 202 | std::string brand(SupplementaryBranding::GetBrand()); 203 | if (brand.empty()) 204 | return dict_; 205 | 206 | NSString* brand_ns = 207 | [@"brand_" stringByAppendingString:base::SysUTF8ToNSString(brand)]; 208 | 209 | return GetOrCreateDict(dict_.get(), brand_ns); 210 | } 211 | 212 | NSMutableDictionary* RlzValueStoreMac::ProductDict(Product p) { 213 | return GetOrCreateDict(WorkingDict(), GetNSProductName(p)); 214 | } 215 | 216 | 217 | namespace { 218 | 219 | // Creating a recursive cross-process mutex on windows is one line. On mac, 220 | // there's no primitve for that, so this lock is emulated by an in-process 221 | // mutex to get the recursive part, followed by a cross-process lock for the 222 | // cross-process part. 223 | 224 | // This is a struct so that it doesn't need a static initializer. 225 | struct RecursiveCrossProcessLock { 226 | // Tries to acquire a recursive cross-process lock. Note that this _always_ 227 | // acquires the in-process lock (if it wasn't already acquired). The parent 228 | // directory of |lock_file| must exist. 229 | bool TryGetCrossProcessLock(NSString* lock_filename); 230 | 231 | // Releases the lock. Should always be called, even if 232 | // TryGetCrossProcessLock() returns false. 233 | void ReleaseLock(); 234 | 235 | pthread_mutex_t recursive_lock_; 236 | pthread_t locking_thread_; 237 | 238 | NSDistributedLock* file_lock_; 239 | } g_recursive_lock = { 240 | // PTHREAD_RECURSIVE_MUTEX_INITIALIZER doesn't exist before 10.7 and is buggy 241 | // on 10.7 (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51906#c34), so emulate 242 | // recursive locking with a normal non-recursive mutex. 243 | PTHREAD_MUTEX_INITIALIZER 244 | }; 245 | 246 | bool RecursiveCrossProcessLock::TryGetCrossProcessLock( 247 | NSString* lock_filename) { 248 | bool just_got_lock = false; 249 | 250 | // Emulate a recursive mutex with a non-recursive one. 251 | if (pthread_mutex_trylock(&recursive_lock_) == EBUSY) { 252 | if (pthread_equal(pthread_self(), locking_thread_) == 0) { 253 | // Some other thread has the lock, wait for it. 254 | pthread_mutex_lock(&recursive_lock_); 255 | CHECK(locking_thread_ == 0); 256 | just_got_lock = true; 257 | } 258 | } else { 259 | just_got_lock = true; 260 | } 261 | 262 | locking_thread_ = pthread_self(); 263 | 264 | // Try to acquire file lock. 265 | if (just_got_lock) { 266 | const int kMaxTimeoutMS = 5000; // Matches windows. 267 | const int kSleepPerTryMS = 200; 268 | 269 | CHECK(!file_lock_); 270 | file_lock_ = [[NSDistributedLock alloc] initWithPath:lock_filename]; 271 | 272 | BOOL got_file_lock = NO; 273 | int elapsedMS = 0; 274 | while (!(got_file_lock = [file_lock_ tryLock]) && 275 | elapsedMS < kMaxTimeoutMS) { 276 | usleep(kSleepPerTryMS * 1000); 277 | elapsedMS += kSleepPerTryMS; 278 | } 279 | 280 | if (!got_file_lock) { 281 | [file_lock_ release]; 282 | file_lock_ = nil; 283 | return false; 284 | } 285 | return true; 286 | } else { 287 | return file_lock_ != nil; 288 | } 289 | } 290 | 291 | void RecursiveCrossProcessLock::ReleaseLock() { 292 | if (file_lock_) { 293 | [file_lock_ unlock]; 294 | [file_lock_ release]; 295 | file_lock_ = nil; 296 | } 297 | 298 | locking_thread_ = 0; 299 | pthread_mutex_unlock(&recursive_lock_); 300 | } 301 | 302 | 303 | // This is set during test execution, to write RLZ files into a temporary 304 | // directory instead of the user's Application Support folder. 305 | NSString* g_test_folder; 306 | 307 | // RlzValueStoreMac keeps its data in memory and only writes it to disk when 308 | // ScopedRlzValueStoreLock goes out of scope. Hence, if several 309 | // ScopedRlzValueStoreLocks are nested, they all need to use the same store 310 | // object. 311 | 312 | // This counts the nesting depth. 313 | int g_lock_depth = 0; 314 | 315 | // This is the store object that might be shared. Only set if g_lock_depth > 0. 316 | RlzValueStoreMac* g_store_object = NULL; 317 | 318 | 319 | NSString* CreateRlzDirectory() { 320 | NSFileManager* manager = [NSFileManager defaultManager]; 321 | NSArray* paths = NSSearchPathForDirectoriesInDomains( 322 | NSApplicationSupportDirectory, NSUserDomainMask, /*expandTilde=*/YES); 323 | NSString* folder = nil; 324 | if ([paths count] > 0) 325 | folder = ObjCCast([paths objectAtIndex:0]); 326 | if (!folder) 327 | folder = [@"~/Library/Application Support" stringByStandardizingPath]; 328 | folder = [folder stringByAppendingPathComponent:@"Google/RLZ"]; 329 | 330 | if (g_test_folder) 331 | folder = [g_test_folder stringByAppendingPathComponent:folder]; 332 | 333 | [manager createDirectoryAtPath:folder 334 | withIntermediateDirectories:YES 335 | attributes:nil 336 | error:nil]; 337 | return folder; 338 | } 339 | 340 | // Returns the path of the rlz plist store, also creates the parent directory 341 | // path if it doesn't exist. 342 | NSString* RlzPlistFilename() { 343 | NSString* const kRlzFile = @"RlzStore.plist"; 344 | return [CreateRlzDirectory() stringByAppendingPathComponent:kRlzFile]; 345 | } 346 | 347 | // Returns the path of the rlz lock file, also creates the parent directory 348 | // path if it doesn't exist. 349 | NSString* RlzLockFilename() { 350 | NSString* const kRlzFile = @"lockfile"; 351 | return [CreateRlzDirectory() stringByAppendingPathComponent:kRlzFile]; 352 | } 353 | 354 | } // namespace 355 | 356 | ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() { 357 | bool got_distributed_lock = 358 | g_recursive_lock.TryGetCrossProcessLock(RlzLockFilename()); 359 | // At this point, we hold the in-process lock, no matter the value of 360 | // |got_distributed_lock|. 361 | 362 | ++g_lock_depth; 363 | 364 | if (!got_distributed_lock) { 365 | // Give up. |store_| isn't set, which signals to callers that acquiring 366 | // the lock failed. |g_recursive_lock| will be released by the 367 | // destructor. 368 | CHECK(!g_store_object); 369 | return; 370 | } 371 | 372 | if (g_lock_depth > 1) { 373 | // Reuse the already existing store object. 374 | CHECK(g_store_object); 375 | store_.reset(g_store_object); 376 | return; 377 | } 378 | 379 | CHECK(!g_store_object); 380 | 381 | NSString* plist = RlzPlistFilename(); 382 | 383 | // Create an empty file if none exists yet. 384 | NSFileManager* manager = [NSFileManager defaultManager]; 385 | if (![manager fileExistsAtPath:plist isDirectory:NULL]) 386 | [[NSDictionary dictionary] writeToFile:plist atomically:YES]; 387 | 388 | NSMutableDictionary* dict = 389 | [NSMutableDictionary dictionaryWithContentsOfFile:plist]; 390 | VERIFY(dict); 391 | 392 | if (dict) { 393 | store_.reset(new RlzValueStoreMac(dict, plist)); 394 | g_store_object = (RlzValueStoreMac*)store_.get(); 395 | } 396 | } 397 | 398 | ScopedRlzValueStoreLock::~ScopedRlzValueStoreLock() { 399 | --g_lock_depth; 400 | CHECK(g_lock_depth >= 0); 401 | 402 | if (g_lock_depth > 0) { 403 | // Other locks are still using store_, don't free it yet. 404 | ignore_result(store_.release()); 405 | return; 406 | } 407 | 408 | if (store_.get()) { 409 | g_store_object = NULL; 410 | 411 | NSDictionary* dict = 412 | static_cast(store_.get())->dictionary(); 413 | VERIFY([dict writeToFile:RlzPlistFilename() atomically:YES]); 414 | } 415 | 416 | // Check that "store_ set" => "file_lock acquired". The converse isn't true, 417 | // for example if the rlz data file can't be read. 418 | if (store_.get()) 419 | CHECK(g_recursive_lock.file_lock_); 420 | if (!g_recursive_lock.file_lock_) 421 | CHECK(!store_.get()); 422 | 423 | g_recursive_lock.ReleaseLock(); 424 | } 425 | 426 | RlzValueStore* ScopedRlzValueStoreLock::GetStore() { 427 | return store_.get(); 428 | } 429 | 430 | namespace testing { 431 | 432 | void SetRlzStoreDirectory(const FilePath& directory) { 433 | base::mac::ScopedNSAutoreleasePool pool; 434 | 435 | [g_test_folder release]; 436 | if (directory.empty()) { 437 | g_test_folder = nil; 438 | } else { 439 | // Not Unsafe on OS X. 440 | g_test_folder = 441 | [[NSString alloc] initWithUTF8String:directory.AsUTF8Unsafe().c_str()]; 442 | } 443 | } 444 | 445 | } // namespace testing 446 | 447 | } // namespace rlz_lib 448 | -------------------------------------------------------------------------------- /rlz.gyp: -------------------------------------------------------------------------------- 1 | # Copyright 2011 Google Inc. All Rights Reserved. 2 | # Use of this source code is governed by an Apache-style license that can be 3 | # found in the COPYING file. 4 | 5 | { 6 | 'variables': { 7 | 'chromium_code': 1, 8 | 'variables': { 9 | # Set force_rlz_use_chrome_net to 1 to use chrome's network stack instead 10 | # of win inet. 11 | 'force_rlz_use_chrome_net%': 0, 12 | }, 13 | 'conditions': [ 14 | ['force_rlz_use_chrome_net or OS=="mac"', { 15 | 'rlz_use_chrome_net%': 1, 16 | }, { 17 | 'rlz_use_chrome_net%': 0, 18 | }], 19 | ], 20 | }, 21 | 'target_defaults': { 22 | 'include_dirs': [ 23 | '..', 24 | ], 25 | }, 26 | 'targets': [ 27 | { 28 | 'target_name': 'rlz_lib', 29 | 'type': 'static_library', 30 | 'include_dirs': [], 31 | 'dependencies': [ 32 | '../base/base.gyp:base', 33 | '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', 34 | ], 35 | 'sources': [ 36 | 'lib/assert.cc', 37 | 'lib/assert.h', 38 | 'lib/crc32.h', 39 | 'lib/crc32_wrapper.cc', 40 | 'lib/crc8.h', 41 | 'lib/crc8.cc', 42 | 'lib/financial_ping.cc', 43 | 'lib/financial_ping.h', 44 | 'lib/lib_values.cc', 45 | 'lib/machine_id.cc', 46 | 'lib/machine_id.h', 47 | 'lib/rlz_enums.h', 48 | 'lib/rlz_lib.cc', 49 | 'lib/rlz_lib.h', 50 | 'lib/rlz_lib_clear.cc', 51 | 'lib/lib_values.h', 52 | 'lib/rlz_value_store.h', 53 | 'lib/string_utils.cc', 54 | 'lib/string_utils.h', 55 | 'mac/lib/machine_id_mac.cc', 56 | 'mac/lib/rlz_value_store_mac.mm', 57 | 'mac/lib/rlz_value_store_mac.h', 58 | 'win/lib/lib_mutex.cc', 59 | 'win/lib/lib_mutex.h', 60 | 'win/lib/machine_deal.cc', 61 | 'win/lib/machine_deal.h', 62 | 'win/lib/machine_id_win.cc', 63 | 'win/lib/process_info.cc', 64 | 'win/lib/process_info.h', 65 | 'win/lib/registry_util.cc', 66 | 'win/lib/registry_util.h', 67 | 'win/lib/rlz_lib.h', 68 | 'win/lib/rlz_lib_win.cc', 69 | 'win/lib/rlz_value_store_registry.cc', 70 | 'win/lib/rlz_value_store_registry.h', 71 | 'win/lib/vista_winnt.h', 72 | ], 73 | 'conditions': [ 74 | ['rlz_use_chrome_net==1', { 75 | 'defines': [ 76 | 'RLZ_NETWORK_IMPLEMENTATION_CHROME_NET', 77 | ], 78 | 'direct_dependent_settings': { 79 | 'defines': [ 80 | 'RLZ_NETWORK_IMPLEMENTATION_CHROME_NET', 81 | ], 82 | }, 83 | 'dependencies': [ 84 | '../build/temp_gyp/googleurl.gyp:googleurl', 85 | '../net/net.gyp:net', 86 | ], 87 | }, { 88 | 'defines': [ 89 | 'RLZ_NETWORK_IMPLEMENTATION_WIN_INET', 90 | ], 91 | 'direct_dependent_settings': { 92 | 'defines': [ 93 | 'RLZ_NETWORK_IMPLEMENTATION_WIN_INET', 94 | ], 95 | }, 96 | }], 97 | ], 98 | }, 99 | { 100 | 'target_name': 'rlz_unittests', 101 | 'type': 'executable', 102 | 'include_dirs': [], 103 | 'dependencies': [ 104 | ':rlz_lib', 105 | '../base/base.gyp:base', 106 | '../testing/gmock.gyp:gmock', 107 | '../testing/gtest.gyp:gtest', 108 | '../third_party/zlib/zlib.gyp:zlib', 109 | ], 110 | 'sources': [ 111 | 'lib/crc32_unittest.cc', 112 | 'lib/crc8_unittest.cc', 113 | 'lib/financial_ping_test.cc', 114 | 'lib/lib_values_unittest.cc', 115 | 'lib/machine_id_unittest.cc', 116 | 'lib/rlz_lib_test.cc', 117 | 'lib/string_utils_unittest.cc', 118 | 'test/rlz_test_helpers.cc', 119 | 'test/rlz_test_helpers.h', 120 | 'test/rlz_unittest_main.cc', 121 | 'win/lib/machine_deal_test.cc', 122 | ], 123 | 'conditions': [ 124 | ['rlz_use_chrome_net==1', { 125 | 'dependencies': [ 126 | '../net/net.gyp:net_test_support', 127 | ], 128 | }] 129 | ], 130 | }, 131 | ], 132 | 'conditions': [ 133 | ['OS=="win"', { 134 | 'targets': [ 135 | { 136 | 'target_name': 'rlz', 137 | 'type': 'shared_library', 138 | 'include_dirs': [], 139 | 'sources': [ 140 | 'win/dll/dll_main.cc', 141 | 'win/dll/exports.cc', 142 | ], 143 | 'dependencies': [ 144 | ':rlz_lib', 145 | '../third_party/zlib/zlib.gyp:zlib', 146 | ], 147 | }, 148 | ], 149 | }], 150 | ], 151 | } 152 | -------------------------------------------------------------------------------- /test/rlz_test_helpers.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // Main entry point for all unit tests. 6 | 7 | #include "rlz_test_helpers.h" 8 | 9 | #include "rlz/lib/rlz_lib.h" 10 | #include "testing/gtest/include/gtest/gtest.h" 11 | 12 | #if defined(OS_WIN) 13 | #include 14 | #include "base/win/registry.h" 15 | #include "rlz/win/lib/rlz_lib.h" 16 | #elif defined(OS_MACOSX) 17 | #include "base/file_path.h" 18 | #include "rlz/lib/rlz_value_store.h" 19 | #endif 20 | 21 | #if defined(OS_WIN) 22 | namespace { 23 | 24 | const wchar_t* kHKCUReplacement = L"Software\\Google\\RlzUtilUnittest\\HKCU"; 25 | const wchar_t* kHKLMReplacement = L"Software\\Google\\RlzUtilUnittest\\HKLM"; 26 | 27 | void OverrideRegistryHives() { 28 | // Wipe the keys we redirect to. 29 | // This gives us a stable run, even in the presence of previous 30 | // crashes or failures. 31 | LSTATUS err = SHDeleteKey(HKEY_CURRENT_USER, kHKCUReplacement); 32 | EXPECT_TRUE(err == ERROR_SUCCESS || err == ERROR_FILE_NOT_FOUND); 33 | err = SHDeleteKey(HKEY_CURRENT_USER, kHKLMReplacement); 34 | EXPECT_TRUE(err == ERROR_SUCCESS || err == ERROR_FILE_NOT_FOUND); 35 | 36 | // Create the keys we're redirecting HKCU and HKLM to. 37 | base::win::RegKey hkcu; 38 | base::win::RegKey hklm; 39 | ASSERT_EQ(ERROR_SUCCESS, 40 | hkcu.Create(HKEY_CURRENT_USER, kHKCUReplacement, KEY_READ)); 41 | ASSERT_EQ(ERROR_SUCCESS, 42 | hklm.Create(HKEY_CURRENT_USER, kHKLMReplacement, KEY_READ)); 43 | 44 | rlz_lib::InitializeTempHivesForTesting(hklm, hkcu); 45 | 46 | // And do the switcharoo. 47 | ASSERT_EQ(ERROR_SUCCESS, 48 | ::RegOverridePredefKey(HKEY_CURRENT_USER, hkcu.Handle())); 49 | ASSERT_EQ(ERROR_SUCCESS, 50 | ::RegOverridePredefKey(HKEY_LOCAL_MACHINE, hklm.Handle())); 51 | } 52 | 53 | void UndoOverrideRegistryHives() { 54 | // Undo the redirection. 55 | EXPECT_EQ(ERROR_SUCCESS, ::RegOverridePredefKey(HKEY_CURRENT_USER, NULL)); 56 | EXPECT_EQ(ERROR_SUCCESS, ::RegOverridePredefKey(HKEY_LOCAL_MACHINE, NULL)); 57 | } 58 | 59 | } // namespace 60 | #endif // defined(OS_WIN) 61 | 62 | 63 | void RlzLibTestNoMachineState::SetUp() { 64 | #if defined(OS_WIN) 65 | OverrideRegistryHives(); 66 | #elif defined(OS_MACOSX) 67 | base::mac::ScopedNSAutoreleasePool pool; 68 | ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 69 | rlz_lib::testing::SetRlzStoreDirectory(temp_dir_.path()); 70 | #endif // defined(OS_WIN) 71 | } 72 | 73 | void RlzLibTestNoMachineState::TearDown() { 74 | #if defined(OS_WIN) 75 | UndoOverrideRegistryHives(); 76 | #elif defined(OS_MACOSX) 77 | rlz_lib::testing::SetRlzStoreDirectory(FilePath()); 78 | #endif // defined(OS_WIN) 79 | } 80 | 81 | void RlzLibTestBase::SetUp() { 82 | RlzLibTestNoMachineState::SetUp(); 83 | #if defined(OS_WIN) 84 | rlz_lib::CreateMachineState(); 85 | #endif // defined(OS_WIN) 86 | } 87 | -------------------------------------------------------------------------------- /test/rlz_test_helpers.h: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // Helper functions used by the tests. 6 | 7 | #ifndef RLZ_TEST_RLZ_TEST_HELPERS_H 8 | #define RLZ_TEST_RLZ_TEST_HELPERS_H 9 | 10 | #include "base/compiler_specific.h" 11 | #include "testing/gtest/include/gtest/gtest.h" 12 | 13 | #if defined(OS_MACOSX) 14 | #include "base/scoped_temp_dir.h" 15 | #endif 16 | 17 | class RlzLibTestNoMachineState : public ::testing::Test { 18 | protected: 19 | virtual void SetUp() OVERRIDE; 20 | virtual void TearDown() OVERRIDE; 21 | 22 | 23 | #if defined(OS_MACOSX) 24 | ScopedTempDir temp_dir_; 25 | #endif 26 | }; 27 | 28 | class RlzLibTestBase : public RlzLibTestNoMachineState { 29 | virtual void SetUp() OVERRIDE; 30 | }; 31 | 32 | 33 | #endif // RLZ_TEST_RLZ_TEST_HELPERS_H 34 | -------------------------------------------------------------------------------- /test/rlz_unittest_main.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // Main entry point for all unit tests. 6 | 7 | #include "base/at_exit.h" 8 | #include "base/command_line.h" 9 | #include "rlz/lib/rlz_lib.h" 10 | #include "testing/gmock/include/gmock/gmock.h" 11 | #include "testing/gtest/include/gtest/gtest.h" 12 | 13 | int main(int argc, char **argv) { 14 | base::AtExitManager at_exit; 15 | CommandLine::Init(argc, argv); 16 | 17 | testing::InitGoogleMock(&argc, argv); 18 | testing::InitGoogleTest(&argc, argv); 19 | 20 | int ret = RUN_ALL_TESTS(); 21 | if (ret == 0) { 22 | // Now re-run all the tests using a supplementary brand code. This brand 23 | // code will remain in effect for the lifetime of the branding object. 24 | rlz_lib::SupplementaryBranding branding("TEST"); 25 | ret = RUN_ALL_TESTS(); 26 | } 27 | 28 | return ret; 29 | } 30 | -------------------------------------------------------------------------------- /win/dll/dll_main.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // Main function for the RLZ DLL. 6 | 7 | #ifndef _WIN32_WINNT 8 | #define _WIN32_WINNT 0x0501 9 | #endif 10 | #include 11 | 12 | BOOL APIENTRY DllMain(HANDLE module, DWORD reason, LPVOID reserved) { 13 | return TRUE; 14 | } 15 | 16 | -------------------------------------------------------------------------------- /win/dll/exports.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // Functions exported by the RLZ DLL. 6 | 7 | #include "rlz/win/lib/rlz_lib.h" 8 | 9 | #define RLZ_DLL_EXPORT extern "C" __declspec(dllexport) 10 | 11 | RLZ_DLL_EXPORT bool RecordProductEvent(rlz_lib::Product product, 12 | rlz_lib::AccessPoint point, 13 | rlz_lib::Event event_id) { 14 | return rlz_lib::RecordProductEvent(product, point, event_id); 15 | } 16 | 17 | RLZ_DLL_EXPORT bool GetProductEventsAsCgi(rlz_lib::Product product, 18 | char* unescaped_cgi, 19 | size_t unescaped_cgi_size) { 20 | return rlz_lib::GetProductEventsAsCgi(product, unescaped_cgi, 21 | unescaped_cgi_size); 22 | } 23 | RLZ_DLL_EXPORT bool ClearAllProductEvents(rlz_lib::Product product) { 24 | return rlz_lib::ClearAllProductEvents(product); 25 | } 26 | 27 | RLZ_DLL_EXPORT bool ClearProductEvent(rlz_lib::Product product, 28 | rlz_lib::AccessPoint point, 29 | rlz_lib::Event event_id) { 30 | return rlz_lib::ClearProductEvent(product, point, event_id); 31 | } 32 | 33 | RLZ_DLL_EXPORT bool GetAccessPointRlz(rlz_lib::AccessPoint point, 34 | char* rlz, 35 | size_t rlz_size) { 36 | return rlz_lib::GetAccessPointRlz(point, rlz, rlz_size); 37 | } 38 | 39 | RLZ_DLL_EXPORT bool SetAccessPointRlz(rlz_lib::AccessPoint point, 40 | const char* new_rlz) { 41 | return rlz_lib::SetAccessPointRlz(point, new_rlz); 42 | } 43 | 44 | RLZ_DLL_EXPORT bool CreateMachineState() { 45 | return rlz_lib::CreateMachineState(); 46 | } 47 | 48 | RLZ_DLL_EXPORT bool SetMachineDealCode(const char* dcc) { 49 | return rlz_lib::SetMachineDealCode(dcc); 50 | } 51 | 52 | RLZ_DLL_EXPORT bool GetMachineDealCodeAsCgi(char* cgi, size_t cgi_size) { 53 | return rlz_lib::GetMachineDealCodeAsCgi(cgi, cgi_size); 54 | } 55 | 56 | RLZ_DLL_EXPORT bool GetMachineDealCode2(char* dcc, size_t dcc_size) { 57 | return rlz_lib::GetMachineDealCode(dcc, dcc_size); 58 | } 59 | 60 | RLZ_DLL_EXPORT bool GetPingParams(rlz_lib::Product product, 61 | const rlz_lib::AccessPoint* access_points, 62 | char* unescaped_cgi, 63 | size_t unescaped_cgi_size) { 64 | return rlz_lib::GetPingParams(product, access_points, unescaped_cgi, 65 | unescaped_cgi_size); 66 | } 67 | 68 | RLZ_DLL_EXPORT bool ParsePingResponse(rlz_lib::Product product, 69 | const char* response) { 70 | return rlz_lib::ParsePingResponse(product, response); 71 | } 72 | 73 | RLZ_DLL_EXPORT bool IsPingResponseValid(const char* response, 74 | int* checksum_idx) { 75 | return rlz_lib::IsPingResponseValid(response, checksum_idx); 76 | } 77 | 78 | RLZ_DLL_EXPORT bool SetMachineDealCodeFromPingResponse(const char* response) { 79 | return rlz_lib::SetMachineDealCodeFromPingResponse(response); 80 | } 81 | 82 | RLZ_DLL_EXPORT bool SendFinancialPing(rlz_lib::Product product, 83 | const rlz_lib::AccessPoint* access_points, 84 | const char* product_signature, 85 | const char* product_brand, 86 | const char* product_id, 87 | const char* product_lang, 88 | bool exclude_machine_id) { 89 | return rlz_lib::SendFinancialPing(product, access_points, product_signature, 90 | product_brand, product_id, product_lang, exclude_machine_id); 91 | } 92 | 93 | RLZ_DLL_EXPORT bool SendFinancialPingNoDelay( 94 | rlz_lib::Product product, 95 | const rlz_lib::AccessPoint* access_points, 96 | const char* product_signature, 97 | const char* product_brand, 98 | const char* product_id, 99 | const char* product_lang, 100 | bool exclude_machine_id) { 101 | return rlz_lib::SendFinancialPing(product, access_points, product_signature, 102 | product_brand, product_id, product_lang, exclude_machine_id, true); 103 | } 104 | 105 | RLZ_DLL_EXPORT void ClearProductState( 106 | rlz_lib::Product product, const rlz_lib::AccessPoint* access_points) { 107 | return rlz_lib::ClearProductState(product, access_points); 108 | } 109 | -------------------------------------------------------------------------------- /win/lib/lib_mutex.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // Mutex to guarantee serialization of RLZ key accesses. 6 | 7 | #include "rlz/win/lib/lib_mutex.h" 8 | 9 | #include 10 | #include // For SDDL_REVISION_1, ConvertStringSecurityDescript.. 11 | #include // For SetSecurityInfo 12 | 13 | #include "base/logging.h" 14 | #include "base/win/windows_version.h" 15 | 16 | namespace { 17 | 18 | const wchar_t kMutexName[] = L"{A946A6A9-917E-4949-B9BC-6BADA8C7FD63}"; 19 | 20 | } // namespace anonymous 21 | 22 | namespace rlz_lib { 23 | 24 | // Needed to allow synchronization across integrity levels. 25 | static bool SetObjectToLowIntegrity(HANDLE object, 26 | SE_OBJECT_TYPE type = SE_KERNEL_OBJECT) { 27 | if (base::win::GetVersion() < base::win::VERSION_VISTA) 28 | return true; // Not needed on XP. 29 | 30 | // The LABEL_SECURITY_INFORMATION SDDL SACL to be set for low integrity. 31 | static const wchar_t kLowIntegritySddlSacl[] = L"S:(ML;;NW;;;LW)"; 32 | 33 | bool result = false; 34 | DWORD error = ERROR_SUCCESS; 35 | PSECURITY_DESCRIPTOR security_descriptor = NULL; 36 | PACL sacl = NULL; 37 | BOOL sacl_present = FALSE; 38 | BOOL sacl_defaulted = FALSE; 39 | 40 | if (ConvertStringSecurityDescriptorToSecurityDescriptorW( 41 | kLowIntegritySddlSacl, SDDL_REVISION_1, &security_descriptor, NULL)) { 42 | if (GetSecurityDescriptorSacl(security_descriptor, &sacl_present, 43 | &sacl, &sacl_defaulted)) { 44 | error = SetSecurityInfo(object, type, LABEL_SECURITY_INFORMATION, 45 | NULL, NULL, NULL, sacl); 46 | result = (ERROR_SUCCESS == error); 47 | } 48 | LocalFree(security_descriptor); 49 | } 50 | 51 | return result; 52 | } 53 | 54 | LibMutex::LibMutex() : acquired_(false), mutex_(NULL) { 55 | mutex_ = CreateMutex(NULL, false, kMutexName); 56 | bool result = SetObjectToLowIntegrity(mutex_); 57 | if (result) { 58 | acquired_ = (WAIT_OBJECT_0 == WaitForSingleObject(mutex_, 5000L)); 59 | } 60 | } 61 | 62 | LibMutex::~LibMutex() { 63 | if (acquired_) ReleaseMutex(mutex_); 64 | CloseHandle(mutex_); 65 | } 66 | 67 | } // namespace rlz_lib 68 | -------------------------------------------------------------------------------- /win/lib/lib_mutex.h: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // Mutex to guarantee serialization of RLZ key accesses. 6 | 7 | #ifndef RLZ_WIN_LIB_LIB_MUTEX_H_ 8 | #define RLZ_WIN_LIB_LIB_MUTEX_H_ 9 | 10 | #include 11 | 12 | namespace rlz_lib { 13 | 14 | class LibMutex { 15 | public: 16 | LibMutex(); 17 | ~LibMutex(); 18 | 19 | bool failed(void) { return !acquired_; } 20 | 21 | private: 22 | bool acquired_; 23 | HANDLE mutex_; 24 | }; 25 | 26 | } // namespace rlz_lib 27 | 28 | #endif // RLZ_WIN_LIB_LIB_MUTEX_H_ 29 | -------------------------------------------------------------------------------- /win/lib/machine_deal.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // Library functions related to the OEM Deal Confirmation Code. 6 | 7 | #include "rlz/win/lib/machine_deal.h" 8 | 9 | #include 10 | #include 11 | 12 | #include "base/basictypes.h" 13 | #include "base/memory/scoped_ptr.h" 14 | #include "base/string_split.h" 15 | #include "base/string_util.h" 16 | #include "base/stringprintf.h" 17 | #include "base/win/registry.h" 18 | #include "rlz/lib/assert.h" 19 | #include "rlz/lib/lib_values.h" 20 | #include "rlz/win/lib/lib_mutex.h" 21 | #include "rlz/win/lib/registry_util.h" 22 | #include "rlz/win/lib/rlz_value_store_registry.h" 23 | 24 | const wchar_t kDccValueName[] = L"DCC"; 25 | 26 | namespace { 27 | 28 | // Current DCC can only uses [a-zA-Z0-9_-!@$*();.<>,:] 29 | // We will be more liberal and allow some additional chars, but not url meta 30 | // chars. 31 | bool IsGoodDccChar(char ch) { 32 | if (IsAsciiAlpha(ch) || IsAsciiDigit(ch)) 33 | return true; 34 | 35 | switch (ch) { 36 | case '_': 37 | case '-': 38 | case '!': 39 | case '@': 40 | case '$': 41 | case '*': 42 | case '(': 43 | case ')': 44 | case ';': 45 | case '.': 46 | case '<': 47 | case '>': 48 | case ',': 49 | case ':': 50 | return true; 51 | } 52 | 53 | return false; 54 | } 55 | 56 | // This function will remove bad rlz chars and also limit the max rlz to some 57 | // reasonable size. It also assumes that normalized_dcc is at least 58 | // kMaxDccLength+1 long. 59 | void NormalizeDcc(const char* raw_dcc, char* normalized_dcc) { 60 | int index = 0; 61 | for (; raw_dcc[index] != 0 && index < rlz_lib::kMaxDccLength; ++index) { 62 | char current = raw_dcc[index]; 63 | if (IsGoodDccChar(current)) { 64 | normalized_dcc[index] = current; 65 | } else { 66 | normalized_dcc[index] = '.'; 67 | } 68 | } 69 | 70 | normalized_dcc[index] = 0; 71 | } 72 | 73 | bool GetResponseLine(const char* response_text, int response_length, 74 | int* search_index, std::string* response_line) { 75 | if (!response_line || !search_index || *search_index > response_length) 76 | return false; 77 | 78 | response_line->clear(); 79 | 80 | if (*search_index < 0) 81 | return false; 82 | 83 | int line_begin = *search_index; 84 | const char* line_end = strchr(response_text + line_begin, '\n'); 85 | 86 | if (line_end == NULL || line_end - response_text > response_length) { 87 | line_end = response_text + response_length; 88 | *search_index = -1; 89 | } else { 90 | *search_index = line_end - response_text + 1; 91 | } 92 | 93 | response_line->assign(response_text + line_begin, 94 | line_end - response_text - line_begin); 95 | return true; 96 | } 97 | 98 | bool GetResponseValue(const std::string& response_line, 99 | const std::string& response_key, 100 | std::string* value) { 101 | if (!value) 102 | return false; 103 | 104 | value->clear(); 105 | 106 | if (!StartsWithASCII(response_line, response_key, true)) 107 | return false; 108 | 109 | std::vector tokens; 110 | base::SplitString(response_line, ':', &tokens); 111 | if (tokens.size() != 2) 112 | return false; 113 | 114 | // The first token is the key, the second is the value. The value is already 115 | // trimmed for whitespace. 116 | *value = tokens[1]; 117 | return true; 118 | } 119 | 120 | } // namespace anonymous 121 | 122 | namespace rlz_lib { 123 | 124 | bool MachineDealCode::Set(const char* dcc) { 125 | LibMutex lock; 126 | if (lock.failed()) 127 | return false; 128 | 129 | // TODO: if (!ProcessInfo::CanWriteMachineKey()) return false; 130 | 131 | // Validate the new dcc value. 132 | size_t length = strlen(dcc); 133 | if (length > kMaxDccLength) { 134 | ASSERT_STRING("MachineDealCode::Set: DCC length is exceeds max allowed."); 135 | return false; 136 | } 137 | 138 | base::win::RegKey hklm_key(HKEY_LOCAL_MACHINE, 139 | RlzValueStoreRegistry::GetWideLibKeyName().c_str(), 140 | KEY_READ | KEY_WRITE | KEY_WOW64_32KEY); 141 | if (!hklm_key.Valid()) { 142 | ASSERT_STRING("MachineDealCode::Set: Unable to create / open machine key." 143 | " Did you call rlz_lib::CreateMachineState()?"); 144 | return false; 145 | } 146 | 147 | char normalized_dcc[kMaxDccLength + 1]; 148 | NormalizeDcc(dcc, normalized_dcc); 149 | VERIFY(length == strlen(normalized_dcc)); 150 | 151 | // Write the DCC to HKLM. Note that we need to include the null character 152 | // when writing the string. 153 | if (!RegKeyWriteValue(hklm_key, kDccValueName, normalized_dcc)) { 154 | ASSERT_STRING("MachineDealCode::Set: Could not write the DCC value"); 155 | return false; 156 | } 157 | 158 | return true; 159 | } 160 | 161 | bool MachineDealCode::GetNewCodeFromPingResponse(const char* response, 162 | bool* has_new_dcc, char* new_dcc, int new_dcc_size) { 163 | if (!has_new_dcc || !new_dcc || !new_dcc_size) 164 | return false; 165 | 166 | *has_new_dcc = false; 167 | new_dcc[0] = 0; 168 | 169 | int response_length = -1; 170 | if (!IsPingResponseValid(response, &response_length)) 171 | return false; 172 | 173 | // Get the current DCC value to compare to later) 174 | char stored_dcc[kMaxDccLength + 1]; 175 | if (!Get(stored_dcc, arraysize(stored_dcc))) 176 | stored_dcc[0] = 0; 177 | 178 | int search_index = 0; 179 | std::string response_line; 180 | std::string new_dcc_value; 181 | bool old_dcc_confirmed = false; 182 | const std::string dcc_cgi(kDccCgiVariable); 183 | const std::string dcc_cgi_response(kSetDccResponseVariable); 184 | while (GetResponseLine(response, response_length, &search_index, 185 | &response_line)) { 186 | std::string value; 187 | 188 | if (!old_dcc_confirmed && 189 | GetResponseValue(response_line, dcc_cgi, &value)) { 190 | // This is the old DCC confirmation - should match value in registry. 191 | if (value != stored_dcc) 192 | return false; // Corrupted DCC - ignore this response. 193 | else 194 | old_dcc_confirmed = true; 195 | continue; 196 | } 197 | 198 | if (!(*has_new_dcc) && 199 | GetResponseValue(response_line, dcc_cgi_response, &value)) { 200 | // This is the new DCC. 201 | if (value.size() > kMaxDccLength) continue; // Too long 202 | *has_new_dcc = true; 203 | new_dcc_value = value; 204 | } 205 | } 206 | 207 | old_dcc_confirmed |= (NULL == stored_dcc[0]); 208 | 209 | base::strlcpy(new_dcc, new_dcc_value.c_str(), new_dcc_size); 210 | return old_dcc_confirmed; 211 | } 212 | 213 | bool MachineDealCode::SetFromPingResponse(const char* response) { 214 | bool has_new_dcc = false; 215 | char new_dcc[kMaxDccLength + 1]; 216 | 217 | bool response_valid = GetNewCodeFromPingResponse( 218 | response, &has_new_dcc, new_dcc, arraysize(new_dcc)); 219 | 220 | if (response_valid && has_new_dcc) 221 | return Set(new_dcc); 222 | 223 | return response_valid; 224 | } 225 | 226 | bool MachineDealCode::GetAsCgi(char* cgi, int cgi_size) { 227 | if (!cgi || cgi_size <= 0) { 228 | ASSERT_STRING("MachineDealCode::GetAsCgi: Invalid buffer"); 229 | return false; 230 | } 231 | 232 | cgi[0] = 0; 233 | 234 | std::string cgi_arg; 235 | base::StringAppendF(&cgi_arg, "%s=", kDccCgiVariable); 236 | int cgi_arg_length = cgi_arg.size(); 237 | 238 | if (cgi_arg_length >= cgi_size) { 239 | ASSERT_STRING("MachineDealCode::GetAsCgi: Insufficient buffer size"); 240 | return false; 241 | } 242 | 243 | base::strlcpy(cgi, cgi_arg.c_str(), cgi_size); 244 | 245 | if (!Get(cgi + cgi_arg_length, cgi_size - cgi_arg_length)) { 246 | cgi[0] = 0; 247 | return false; 248 | } 249 | return true; 250 | } 251 | 252 | bool MachineDealCode::Get(char* dcc, int dcc_size) { 253 | LibMutex lock; 254 | if (lock.failed()) 255 | return false; 256 | 257 | if (!dcc || dcc_size <= 0) { 258 | ASSERT_STRING("MachineDealCode::Get: Invalid buffer"); 259 | return false; 260 | } 261 | 262 | dcc[0] = 0; 263 | 264 | base::win::RegKey dcc_key(HKEY_LOCAL_MACHINE, 265 | RlzValueStoreRegistry::GetWideLibKeyName().c_str(), 266 | KEY_READ | KEY_WOW64_32KEY); 267 | if (!dcc_key.Valid()) 268 | return false; // no DCC key. 269 | 270 | size_t size = dcc_size; 271 | if (!RegKeyReadValue(dcc_key, kDccValueName, dcc, &size)) { 272 | ASSERT_STRING("MachineDealCode::Get: Insufficient buffer size"); 273 | dcc[0] = 0; 274 | return false; 275 | } 276 | 277 | return true; 278 | } 279 | 280 | bool MachineDealCode::Clear() { 281 | base::win::RegKey dcc_key(HKEY_LOCAL_MACHINE, 282 | RlzValueStoreRegistry::GetWideLibKeyName().c_str(), 283 | KEY_READ | KEY_WRITE | KEY_WOW64_32KEY); 284 | if (!dcc_key.Valid()) 285 | return false; // no DCC key. 286 | 287 | dcc_key.DeleteValue(kDccValueName); 288 | 289 | // Verify deletion. 290 | wchar_t dcc[kMaxDccLength + 1]; 291 | DWORD dcc_size = arraysize(dcc); 292 | if (dcc_key.ReadValue(kDccValueName, dcc, &dcc_size, NULL) == ERROR_SUCCESS) { 293 | ASSERT_STRING("MachineDealCode::Clear: Could not delete the DCC value."); 294 | return false; 295 | } 296 | 297 | return true; 298 | } 299 | 300 | } // namespace rlz_lib 301 | -------------------------------------------------------------------------------- /win/lib/machine_deal.h: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // Library functions related to the OEM Deal Confirmation Code. 6 | 7 | #ifndef RLZ_WIN_LIB_MACHINE_DEAL_H_ 8 | #define RLZ_WIN_LIB_MACHINE_DEAL_H_ 9 | 10 | #include 11 | #include "rlz/win/lib/rlz_lib.h" 12 | 13 | namespace rlz_lib { 14 | 15 | class MachineDealCode { 16 | public: 17 | // Set the OEM Deal Confirmation Code (DCC). This information is used for RLZ 18 | // initalization. Must have write access to HKLM - SYSTEM or admin, unless 19 | // rlz_lib::CreateMachineState() has been successfully called. 20 | static bool Set(const char* dcc); 21 | 22 | // Get the OEM Deal Confirmation Code from the registry. Used to ping 23 | // the server. 24 | static bool Get(AccessPoint point, 25 | char* dcc, 26 | int dcc_size, 27 | const wchar_t* sid = NULL); 28 | 29 | // Parses a ping response, checks if it is valid and sets the machine DCC 30 | // from the response. The response should also contain the current value of 31 | // the DCC to be considered valid. 32 | // Write access to HKLM (system / admin) needed, unless 33 | // rlz_lib::CreateMachineState() has been successfully called. 34 | static bool SetFromPingResponse(const char* response); 35 | 36 | // Gets the new DCC to set from a ping response. Returns true if the ping 37 | // response is valid. Sets has_new_dcc true if there is a new DCC value. 38 | static bool GetNewCodeFromPingResponse(const char* response, 39 | bool* has_new_dcc, 40 | char* new_dcc, 41 | int new_dcc_size); 42 | 43 | // Get the DCC cgi argument string to append to a daily or financial ping. 44 | static bool GetAsCgi(char* cgi, int cgi_size); 45 | 46 | // Get the machine code. 47 | static bool Get(char* dcc, int dcc_size); 48 | 49 | protected: 50 | // Clear the DCC value. Only for testing purposes. 51 | // Requires write access to HKLM, unless rlz_lib::CreateMachineState() has 52 | // been successfully called. 53 | static bool Clear(); 54 | 55 | MachineDealCode() {} 56 | ~MachineDealCode() {} 57 | }; 58 | 59 | } // namespace rlz_lib 60 | 61 | #endif // RLZ_WIN_LIB_MACHINE_DEAL_H_ 62 | -------------------------------------------------------------------------------- /win/lib/machine_deal_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // A test application for the MachineDealCode class. 6 | // 7 | // These tests should not be executed on the build server: 8 | // - They assert for the failed cases. 9 | // - They modify machine state (registry). 10 | // 11 | // These tests require write access to HKLM and HKCU, unless 12 | // rlz_lib::CreateMachineState() has been successfully called. 13 | 14 | #include "base/logging.h" 15 | #include "testing/gmock/include/gmock/gmock.h" 16 | #include "testing/gtest/include/gtest/gtest.h" 17 | 18 | #include "rlz/test/rlz_test_helpers.h" 19 | #include "rlz/win/lib/machine_deal.h" 20 | 21 | class MachineDealCodeHelper : public rlz_lib::MachineDealCode { 22 | public: 23 | static bool Clear() { return rlz_lib::MachineDealCode::Clear(); } 24 | 25 | private: 26 | MachineDealCodeHelper() {} 27 | ~MachineDealCodeHelper() {} 28 | }; 29 | 30 | class MachineDealCodeTest : public RlzLibTestBase { 31 | }; 32 | 33 | TEST_F(MachineDealCodeTest, CreateMachineState) { 34 | EXPECT_TRUE(rlz_lib::CreateMachineState()); 35 | } 36 | 37 | TEST_F(MachineDealCodeTest, Set) { 38 | MachineDealCodeHelper::Clear(); 39 | char dcc_50[50]; 40 | dcc_50[0] = 0; 41 | 42 | EXPECT_TRUE(rlz_lib::MachineDealCode::Set("dcc_value")); 43 | 44 | EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50)); 45 | EXPECT_STREQ("dcc_value", dcc_50); 46 | 47 | EXPECT_TRUE(rlz_lib::MachineDealCode::Set("dcc_value_2")); 48 | 49 | EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50)); 50 | EXPECT_STREQ("dcc_value_2", dcc_50); 51 | } 52 | 53 | TEST_F(MachineDealCodeTest, Get) { 54 | MachineDealCodeHelper::Clear(); 55 | char dcc_50[50], dcc_2[2]; 56 | dcc_50[0] = 0; 57 | dcc_2[0] = 0; 58 | 59 | EXPECT_FALSE(rlz_lib::MachineDealCode::Get(dcc_50, 50)); 60 | 61 | EXPECT_TRUE(rlz_lib::MachineDealCode::Set("dcc_value")); 62 | 63 | EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50)); 64 | EXPECT_STREQ("dcc_value", dcc_50); 65 | 66 | EXPECT_FALSE(rlz_lib::MachineDealCode::Get(dcc_2, 2)); 67 | } 68 | 69 | TEST_F(MachineDealCodeTest, SetFromPingResponse) { 70 | rlz_lib::MachineDealCode::Set("MyDCCode"); 71 | char dcc_50[50]; 72 | 73 | // Bad responses 74 | 75 | char* kBadDccResponse = 76 | "dcc: NotMyDCCode \r\n" 77 | "set_dcc: NewDCCode\r\n" 78 | "crc32: 1B4D6BB3"; 79 | EXPECT_FALSE(rlz_lib::MachineDealCode::SetFromPingResponse( 80 | kBadDccResponse)); 81 | EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50)); 82 | EXPECT_STREQ("MyDCCode", dcc_50); 83 | 84 | char* kBadCrcResponse = 85 | "dcc: MyDCCode \r\n" 86 | "set_dcc: NewDCCode\r\n" 87 | "crc32: 90707106"; 88 | EXPECT_FALSE(rlz_lib::MachineDealCode::SetFromPingResponse( 89 | kBadCrcResponse)); 90 | EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50)); 91 | EXPECT_STREQ("MyDCCode", dcc_50); 92 | 93 | // Good responses 94 | 95 | char* kMissingSetResponse = 96 | "dcc: MyDCCode \r\n" 97 | "crc32: 35F2E717"; 98 | EXPECT_TRUE(rlz_lib::MachineDealCode::SetFromPingResponse( 99 | kMissingSetResponse)); 100 | EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50)); 101 | EXPECT_STREQ("MyDCCode", dcc_50); 102 | 103 | char* kGoodResponse = 104 | "dcc: MyDCCode \r\n" 105 | "set_dcc: NewDCCode\r\n" 106 | "crc32: C8540E02"; 107 | EXPECT_TRUE(rlz_lib::MachineDealCode::SetFromPingResponse( 108 | kGoodResponse)); 109 | EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50)); 110 | EXPECT_STREQ("NewDCCode", dcc_50); 111 | 112 | char* kGoodResponse2 = 113 | "set_dcc: NewDCCode2 \r\n" 114 | "dcc: NewDCCode \r\n" 115 | "crc32: 60B6409A"; 116 | EXPECT_TRUE(rlz_lib::MachineDealCode::SetFromPingResponse( 117 | kGoodResponse2)); 118 | EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50)); 119 | EXPECT_STREQ("NewDCCode2", dcc_50); 120 | 121 | MachineDealCodeHelper::Clear(); 122 | char* kGoodResponse3 = 123 | "set_dcc: NewDCCode \r\n" 124 | "crc32: 374C1C47"; 125 | EXPECT_TRUE(rlz_lib::MachineDealCode::SetFromPingResponse( 126 | kGoodResponse3)); 127 | EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50)); 128 | EXPECT_STREQ("NewDCCode", dcc_50); 129 | 130 | MachineDealCodeHelper::Clear(); 131 | char* kGoodResponse4 = 132 | "dcc: \r\n" 133 | "set_dcc: NewDCCode \r\n" 134 | "crc32: 0AB1FB39"; 135 | EXPECT_TRUE(rlz_lib::MachineDealCode::SetFromPingResponse( 136 | kGoodResponse4)); 137 | EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50)); 138 | EXPECT_STREQ("NewDCCode", dcc_50); 139 | } 140 | 141 | TEST_F(MachineDealCodeTest, GetAsCgi) { 142 | MachineDealCodeHelper::Clear(); 143 | char cgi_50[50], cgi_2[2]; 144 | cgi_50[0] = 0; 145 | cgi_2[0] = 0; 146 | 147 | EXPECT_FALSE(rlz_lib::MachineDealCode::GetAsCgi(cgi_50, 50)); 148 | EXPECT_STREQ("", cgi_50); 149 | 150 | EXPECT_TRUE(rlz_lib::MachineDealCode::Set("dcc_value")); 151 | 152 | EXPECT_TRUE(rlz_lib::MachineDealCode::GetAsCgi(cgi_50, 50)); 153 | EXPECT_STREQ("dcc=dcc_value", cgi_50); 154 | 155 | EXPECT_FALSE(rlz_lib::MachineDealCode::GetAsCgi(cgi_2, 2)); 156 | } 157 | -------------------------------------------------------------------------------- /win/lib/machine_id_win.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | 5 | #include 6 | #include // For ConvertSidToStringSidW. 7 | #include 8 | 9 | #include "base/memory/scoped_ptr.h" 10 | #include "base/string16.h" 11 | #include "rlz/lib/assert.h" 12 | 13 | namespace rlz_lib { 14 | 15 | namespace { 16 | 17 | bool GetSystemVolumeSerialNumber(int* number) { 18 | if (!number) 19 | return false; 20 | 21 | *number = 0; 22 | 23 | // Find the system root path (e.g: C:\). 24 | wchar_t system_path[MAX_PATH + 1]; 25 | if (!GetSystemDirectoryW(system_path, MAX_PATH)) 26 | return false; 27 | 28 | wchar_t* first_slash = wcspbrk(system_path, L"\\/"); 29 | if (first_slash != NULL) 30 | *(first_slash + 1) = 0; 31 | 32 | DWORD number_local = 0; 33 | if (!GetVolumeInformationW(system_path, NULL, 0, &number_local, NULL, NULL, 34 | NULL, 0)) 35 | return false; 36 | 37 | *number = number_local; 38 | return true; 39 | } 40 | 41 | bool GetComputerSid(const wchar_t* account_name, SID* sid, DWORD sid_size) { 42 | static const DWORD kStartDomainLength = 128; // reasonable to start with 43 | 44 | scoped_array domain_buffer(new wchar_t[kStartDomainLength]); 45 | DWORD domain_size = kStartDomainLength; 46 | DWORD sid_dword_size = sid_size; 47 | SID_NAME_USE sid_name_use; 48 | 49 | BOOL success = ::LookupAccountNameW(NULL, account_name, sid, 50 | &sid_dword_size, domain_buffer.get(), 51 | &domain_size, &sid_name_use); 52 | if (!success && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { 53 | // We could have gotten the insufficient buffer error because 54 | // one or both of sid and szDomain was too small. Check for that 55 | // here. 56 | if (sid_dword_size > sid_size) 57 | return false; 58 | 59 | if (domain_size > kStartDomainLength) 60 | domain_buffer.reset(new wchar_t[domain_size]); 61 | 62 | success = ::LookupAccountNameW(NULL, account_name, sid, &sid_dword_size, 63 | domain_buffer.get(), &domain_size, 64 | &sid_name_use); 65 | } 66 | 67 | return success != FALSE; 68 | } 69 | 70 | std::wstring ConvertSidToString(SID* sid) { 71 | std::wstring sid_string; 72 | #if _WIN32_WINNT >= 0x500 73 | wchar_t* sid_buffer = NULL; 74 | if (ConvertSidToStringSidW(sid, &sid_buffer)) { 75 | sid_string = sid_buffer; 76 | LocalFree(sid_buffer); 77 | } 78 | #else 79 | SID_IDENTIFIER_AUTHORITY* sia = ::GetSidIdentifierAuthority(sid); 80 | 81 | if(sia->Value[0] || sia->Value[1]) { 82 | base::SStringPrintf( 83 | &sid_string, L"S-%d-0x%02hx%02hx%02hx%02hx%02hx%02hx", 84 | SID_REVISION, (USHORT)sia->Value[0], (USHORT)sia->Value[1], 85 | (USHORT)sia->Value[2], (USHORT)sia->Value[3], (USHORT)sia->Value[4], 86 | (USHORT)sia->Value[5]); 87 | } else { 88 | ULONG authority = 0; 89 | for (int i = 2; i < 6; ++i) { 90 | authority <<= 8; 91 | authority |= sia->Value[i]; 92 | } 93 | base::SStringPrintf(&sid_string, L"S-%d-%lu", SID_REVISION, authority); 94 | } 95 | 96 | int sub_auth_count = *::GetSidSubAuthorityCount(sid); 97 | for(int i = 0; i < sub_auth_count; ++i) 98 | base::StringAppendF(&sid_string, L"-%lu", *::GetSidSubAuthority(sid, i)); 99 | #endif 100 | 101 | return sid_string; 102 | } 103 | 104 | } // namespace 105 | 106 | bool GetRawMachineId(string16* sid_string, int* volume_id) { 107 | // Calculate the Windows SID. 108 | 109 | wchar_t computer_name[MAX_COMPUTERNAME_LENGTH + 1] = {0}; 110 | DWORD size = arraysize(computer_name); 111 | 112 | if (GetComputerNameW(computer_name, &size)) { 113 | char sid_buffer[SECURITY_MAX_SID_SIZE]; 114 | SID* sid = reinterpret_cast(sid_buffer); 115 | if (GetComputerSid(computer_name, sid, SECURITY_MAX_SID_SIZE)) { 116 | *sid_string = ConvertSidToString(sid); 117 | } 118 | } 119 | 120 | // Get the system drive volume serial number. 121 | *volume_id = 0; 122 | if (!GetSystemVolumeSerialNumber(volume_id)) { 123 | ASSERT_STRING("GetMachineId: Failed to retrieve volume serial number"); 124 | *volume_id = 0; 125 | } 126 | 127 | return true; 128 | } 129 | 130 | } // namespace rlz_lib 131 | -------------------------------------------------------------------------------- /win/lib/process_info.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // Information about the current process. 6 | 7 | #include "rlz/win/lib/process_info.h" 8 | 9 | #include 10 | #include // For ConvertSidToStringSid. 11 | #include // For UNLEN 12 | 13 | #include "base/logging.h" 14 | #include "base/memory/scoped_ptr.h" 15 | #include "base/process_util.h" 16 | #include "base/win/scoped_handle.h" 17 | #include "base/win/windows_version.h" 18 | #include "rlz/lib/assert.h" 19 | #include "rlz/win/lib/vista_winnt.h" 20 | 21 | namespace { 22 | 23 | HRESULT GetCurrentUser(std::wstring* name, 24 | std::wstring* domain, 25 | std::wstring* sid) { 26 | DWORD err; 27 | 28 | // Get the current username & domain the hard way. (GetUserNameEx would be 29 | // nice, but unfortunately requires connectivity to a domain controller. 30 | // Useless.) 31 | 32 | // (Following call doesn't work if running as a Service - because a Service 33 | // runs under special accounts like LOCAL_SYSTEM, not as the logged in user. 34 | // In which case, search for and use the process handle of a running 35 | // Explorer.exe.) 36 | HANDLE token; 37 | if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token)) 38 | return E_FAIL; 39 | 40 | base::win::ScopedHandle scoped_process_token(token); 41 | 42 | // (Following call will fail with ERROR_INSUFFICIENT_BUFFER and give us the 43 | // required size.) 44 | scoped_array token_user_bytes; 45 | DWORD token_user_size; 46 | DWORD token_user_size2; 47 | BOOL result = ::GetTokenInformation(token, TokenUser, NULL, 0, 48 | &token_user_size); 49 | err = ::GetLastError(); 50 | CHECK(!result && err == ERROR_INSUFFICIENT_BUFFER); 51 | 52 | token_user_bytes.reset(new char[token_user_size]); 53 | if (!token_user_bytes.get()) 54 | return E_OUTOFMEMORY; 55 | 56 | if (!::GetTokenInformation(token, TokenUser, token_user_bytes.get(), 57 | token_user_size, &token_user_size2)) { 58 | return E_FAIL; 59 | } 60 | 61 | WCHAR user_name[UNLEN + 1]; // max username length 62 | WCHAR domain_name[UNLEN + 1]; 63 | DWORD user_name_size = UNLEN + 1; 64 | DWORD domain_name_size = UNLEN + 1; 65 | SID_NAME_USE sid_type; 66 | TOKEN_USER* token_user = 67 | reinterpret_cast(token_user_bytes.get()); 68 | if (!token_user) 69 | return E_FAIL; 70 | PSID user_sid = token_user->User.Sid; 71 | if (!::LookupAccountSidW(NULL, user_sid, user_name, &user_name_size, 72 | domain_name, &domain_name_size, &sid_type)) { 73 | return E_FAIL; 74 | } 75 | 76 | if (name != NULL) { 77 | *name = user_name; 78 | } 79 | if (domain != NULL) { 80 | *domain = domain_name; 81 | } 82 | if (sid != NULL) { 83 | LPWSTR string_sid; 84 | ConvertSidToStringSidW(user_sid, &string_sid); 85 | *sid = string_sid; // copy out to cstring 86 | // free memory, as documented for ConvertSidToStringSid 87 | LocalFree(string_sid); 88 | } 89 | 90 | return S_OK; 91 | } 92 | 93 | HRESULT GetElevationType(PTOKEN_ELEVATION_TYPE elevation) { 94 | if (!elevation) 95 | return E_POINTER; 96 | 97 | *elevation = TokenElevationTypeDefault; 98 | 99 | if (base::win::GetVersion() < base::win::VERSION_VISTA) 100 | return E_FAIL; 101 | 102 | HANDLE process_token; 103 | if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &process_token)) 104 | return HRESULT_FROM_WIN32(GetLastError()); 105 | 106 | base::win::ScopedHandle scoped_process_token(process_token); 107 | 108 | DWORD size; 109 | TOKEN_ELEVATION_TYPE elevation_type; 110 | if (!GetTokenInformation(process_token, TokenElevationType, &elevation_type, 111 | sizeof(elevation_type), &size)) { 112 | return HRESULT_FROM_WIN32(GetLastError()); 113 | } 114 | 115 | *elevation = elevation_type; 116 | return S_OK; 117 | } 118 | 119 | // based on http://msdn2.microsoft.com/en-us/library/aa376389.aspx 120 | bool GetUserGroup(long* group) { 121 | if (!group) 122 | return false; 123 | 124 | *group = 0; 125 | 126 | // groups are listed in DECREASING order of importance 127 | // (eg. If a user is a member of both the admin group and 128 | // the power user group, it is more useful to list the user 129 | // as an admin) 130 | DWORD user_groups[] = {DOMAIN_ALIAS_RID_ADMINS, 131 | DOMAIN_ALIAS_RID_POWER_USERS}; 132 | SID_IDENTIFIER_AUTHORITY nt_authority = SECURITY_NT_AUTHORITY; 133 | 134 | for (int i = 0; i < arraysize(user_groups) && *group == 0; ++i) { 135 | PSID current_group; 136 | if (AllocateAndInitializeSid(&nt_authority, 2, 137 | SECURITY_BUILTIN_DOMAIN_RID, 138 | user_groups[i], 0, 0, 0, 0, 139 | 0, 0, ¤t_group)) { 140 | BOOL current_level; 141 | if (CheckTokenMembership(NULL, current_group, ¤t_level) && 142 | current_level) { 143 | *group = user_groups[i]; 144 | } 145 | 146 | FreeSid(current_group); 147 | } 148 | } 149 | 150 | return group != 0; 151 | } 152 | } //anonymous 153 | 154 | 155 | namespace rlz_lib { 156 | 157 | bool ProcessInfo::IsRunningAsSystem() { 158 | static std::wstring name; 159 | static std::wstring domain; 160 | static std::wstring sid; 161 | if (name.empty()) 162 | CHECK(SUCCEEDED(GetCurrentUser(&name, &domain, &sid))); 163 | 164 | return (name == L"SYSTEM"); 165 | } 166 | 167 | bool ProcessInfo::HasAdminRights() { 168 | static bool evaluated = false; 169 | static bool has_rights = false; 170 | 171 | if (!evaluated) { 172 | if (IsRunningAsSystem()) { 173 | has_rights = true; 174 | } else if (base::win::GetVersion() >= base::win::VERSION_VISTA) { 175 | TOKEN_ELEVATION_TYPE elevation; 176 | base::IntegrityLevel level; 177 | 178 | if (SUCCEEDED(GetElevationType(&elevation)) && 179 | base::GetProcessIntegrityLevel(base::GetCurrentProcessHandle(), &level)) 180 | has_rights = (elevation == TokenElevationTypeFull) || 181 | (level == HIGH_INTEGRITY); 182 | } else { 183 | long group = 0; 184 | if (GetUserGroup(&group)) 185 | has_rights = (group == DOMAIN_ALIAS_RID_ADMINS); 186 | } 187 | } 188 | 189 | evaluated = true; 190 | if (!has_rights) 191 | ASSERT_STRING("ProcessInfo::HasAdminRights: Does not have admin rights."); 192 | 193 | return has_rights; 194 | } 195 | 196 | }; // namespace 197 | -------------------------------------------------------------------------------- /win/lib/process_info.h: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // Information about the current process. 6 | 7 | #ifndef RLZ_WIN_LIB_PROCESS_INFO_H_ 8 | #define RLZ_WIN_LIB_PROCESS_INFO_H_ 9 | 10 | #include "base/basictypes.h" 11 | 12 | namespace rlz_lib { 13 | 14 | class ProcessInfo { 15 | public: 16 | enum IntegrityLevel { 17 | INTEGRITY_UNKNOWN, 18 | LOW_INTEGRITY, 19 | MEDIUM_INTEGRITY, 20 | HIGH_INTEGRITY, 21 | }; 22 | 23 | // All these functions cache the result after first run. 24 | static bool IsRunningAsSystem(); 25 | static bool HasAdminRights(); // System / Admin / High Elevation on Vista 26 | 27 | private: 28 | DISALLOW_COPY_AND_ASSIGN(ProcessInfo); 29 | }; // class 30 | }; // namespace 31 | 32 | #endif // RLZ_WIN_LIB_PROCESS_INFO_H_ 33 | -------------------------------------------------------------------------------- /win/lib/registry_util.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // A helper library to keep track of a user's key by SID. 6 | // Used by RLZ libary. Also to be used by SearchWithGoogle library. 7 | 8 | #include "rlz/win/lib/registry_util.h" 9 | 10 | #include "base/process_util.h" 11 | #include "base/utf_string_conversions.h" 12 | #include "base/win/registry.h" 13 | #include "base/win/windows_version.h" 14 | #include "rlz/lib/assert.h" 15 | #include "rlz/win/lib/process_info.h" 16 | 17 | namespace rlz_lib { 18 | 19 | bool RegKeyReadValue(base::win::RegKey& key, const wchar_t* name, 20 | char* value, size_t* value_size) { 21 | value[0] = 0; 22 | 23 | std::wstring value_string; 24 | if (key.ReadValue(name, &value_string) != ERROR_SUCCESS) { 25 | return false; 26 | } 27 | 28 | if (value_string.length() > *value_size) { 29 | *value_size = value_string.length(); 30 | return false; 31 | } 32 | 33 | // Note that RLZ string are always ASCII by design. 34 | strncpy(value, WideToUTF8(value_string).c_str(), *value_size); 35 | value[*value_size - 1] = 0; 36 | return true; 37 | } 38 | 39 | bool RegKeyWriteValue(base::win::RegKey& key, const wchar_t* name, 40 | const char* value) { 41 | std::wstring value_string(ASCIIToWide(value)); 42 | return key.WriteValue(name, value_string.c_str()) == ERROR_SUCCESS; 43 | } 44 | 45 | bool HasUserKeyAccess(bool write_access) { 46 | // The caller is trying to access HKEY_CURRENT_USER. Test to see if we can 47 | // read from there. Don't try HKEY_CURRENT_USER because this will cause 48 | // problems in the unit tests: if we open HKEY_CURRENT_USER directly here, 49 | // the overriding done for unit tests will no longer work. So we try subkey 50 | // "Software" which is known to always exist. 51 | base::win::RegKey key; 52 | if (key.Open(HKEY_CURRENT_USER, L"Software", KEY_READ) != ERROR_SUCCESS) 53 | ASSERT_STRING("Could not open HKEY_CURRENT_USER"); 54 | 55 | if (ProcessInfo::IsRunningAsSystem()) { 56 | ASSERT_STRING("UserKey::HasAccess: No access as SYSTEM without SID set."); 57 | return false; 58 | } 59 | 60 | if (write_access) { 61 | if (base::win::GetVersion() < base::win::VERSION_VISTA) return true; 62 | base::ProcessHandle process_handle = base::GetCurrentProcessHandle(); 63 | base::IntegrityLevel level = base::INTEGRITY_UNKNOWN; 64 | 65 | if (!base::GetProcessIntegrityLevel(process_handle, &level)) { 66 | ASSERT_STRING("UserKey::HasAccess: Cannot determine Integrity Level."); 67 | return false; 68 | } 69 | if (level <= base::LOW_INTEGRITY) { 70 | ASSERT_STRING("UserKey::HasAccess: Cannot write from Low Integrity."); 71 | return false; 72 | } 73 | } 74 | return true; 75 | } 76 | 77 | } // namespace rlz_lib 78 | -------------------------------------------------------------------------------- /win/lib/registry_util.h: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | 5 | #ifndef RLZ_WIN_LIB_REGISTRY_UTIL_H_ 6 | #define RLZ_WIN_LIB_REGISTRY_UTIL_H_ 7 | 8 | namespace base { 9 | namespace win { 10 | class RegKey; 11 | } // namespace win 12 | } // namespace base 13 | 14 | namespace rlz_lib { 15 | 16 | bool RegKeyReadValue(base::win::RegKey& key, 17 | const wchar_t* name, 18 | char* value, 19 | size_t* value_size); 20 | 21 | bool RegKeyWriteValue(base::win::RegKey& key, 22 | const wchar_t* name, 23 | const char* value); 24 | 25 | bool HasUserKeyAccess(bool write_access); 26 | 27 | } // namespace rlz_lib 28 | 29 | #endif // RLZ_WIN_LIB_REGISTRY_UTIL_H_ 30 | -------------------------------------------------------------------------------- /win/lib/rlz_lib.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // A library to manage RLZ information for access-points shared 6 | // across different client applications. 7 | // 8 | // All functions return true on success and false on error. 9 | // This implemenation is thread safe. 10 | // 11 | // Each prototype mentions the registry access requirements: 12 | // 13 | // HKLM read: Will work from any process and at any privilege level on Vista. 14 | // HKCU read: Calls made from the SYSTEM account must pass the current user's 15 | // SID as the optional 'sid' param. Can be called from low integrity 16 | // process on Vista. 17 | // HKCU write: Calls made from the SYSTEM account must pass the current user's 18 | // SID as the optional 'sid' param. Calls require at least medium 19 | // integrity on Vista (e.g. Toolbar will need to use their broker) 20 | // HKLM write: Calls must be made from an account with admin rights. No SID 21 | // need be passed when running as SYSTEM. 22 | // Functions which do not access registry will be marked with "no restrictions". 23 | 24 | #ifndef RLZ_WIN_LIB_RLZ_LIB_H_ 25 | #define RLZ_WIN_LIB_RLZ_LIB_H_ 26 | 27 | // Clients can get away by just including rlz/lib/rlz_lib.h. This file only 28 | // contains function definitions for files used by tests. It's mostly kept 29 | // around for backwards-compatibility. 30 | 31 | #include "rlz/lib/rlz_lib.h" 32 | 33 | #include "base/win/registry.h" 34 | 35 | namespace rlz_lib { 36 | 37 | #if defined(OS_WIN) 38 | 39 | // Initialize temporary HKLM/HKCU registry hives used for testing. 40 | // Testing RLZ requires reading and writing to the Windows registry. To keep 41 | // the tests isolated from the machine's state, as well as to prevent the tests 42 | // from causing side effects in the registry, HKCU and HKLM are overridden for 43 | // the duration of the tests. RLZ tests don't expect the HKCU and KHLM hives to 44 | // be empty though, and this function initializes the minimum value needed so 45 | // that the test will run successfully. 46 | // 47 | // The two arguments to this function should be the keys that will represent 48 | // the HKLM and HKCU registry hives during the tests. This function should be 49 | // called *before* the hives are overridden. 50 | void InitializeTempHivesForTesting(const base::win::RegKey& temp_hklm_key, 51 | const base::win::RegKey& temp_hkcu_key); 52 | #endif // defined(OS_WIN) 53 | 54 | } // namespace rlz_lib 55 | 56 | #endif // RLZ_WIN_LIB_RLZ_LIB_H_ 57 | -------------------------------------------------------------------------------- /win/lib/rlz_lib_win.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // A library to manage RLZ information for access-points shared 6 | // across different client applications. 7 | 8 | #include "rlz/win/lib/rlz_lib.h" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "base/basictypes.h" 15 | #include "base/win/registry.h" 16 | #include "base/win/windows_version.h" 17 | #include "rlz/lib/assert.h" 18 | #include "rlz/lib/rlz_value_store.h" 19 | #include "rlz/win/lib/machine_deal.h" 20 | #include "rlz/win/lib/rlz_value_store_registry.h" 21 | 22 | namespace { 23 | 24 | // Path to recursively copy into the replacemment hives. These are needed 25 | // to make sure certain win32 APIs continue to run correctly once the real 26 | // hives are replaced. 27 | const wchar_t* kHKLMAccessProviders = 28 | L"System\\CurrentControlSet\\Control\\Lsa\\AccessProviders"; 29 | 30 | // Helper functions 31 | 32 | void CopyRegistryTree(const base::win::RegKey& src, base::win::RegKey* dest) { 33 | // First copy values. 34 | for (base::win::RegistryValueIterator i(src.Handle(), L""); 35 | i.Valid(); ++i) { 36 | dest->WriteValue(i.Name(), reinterpret_cast(i.Value()), 37 | i.ValueSize(), i.Type()); 38 | } 39 | 40 | // Next copy subkeys recursively. 41 | for (base::win::RegistryKeyIterator i(src.Handle(), L""); 42 | i.Valid(); ++i) { 43 | base::win::RegKey subkey(dest->Handle(), i.Name(), KEY_ALL_ACCESS); 44 | CopyRegistryTree(base::win::RegKey(src.Handle(), i.Name(), KEY_READ), 45 | &subkey); 46 | } 47 | } 48 | 49 | } // namespace anonymous 50 | 51 | 52 | namespace rlz_lib { 53 | 54 | // OEM Deal confirmation storage functions. 55 | 56 | template 57 | class typed_buffer_ptr { 58 | scoped_array buffer_; 59 | 60 | public: 61 | typed_buffer_ptr() { 62 | } 63 | 64 | explicit typed_buffer_ptr(size_t size) : buffer_(new char[size]) { 65 | } 66 | 67 | void reset(size_t size) { 68 | buffer_.reset(new char[size]); 69 | } 70 | 71 | operator T*() { 72 | return reinterpret_cast(buffer_.get()); 73 | } 74 | }; 75 | 76 | // Check if this SID has the desired access by scanning the ACEs in the DACL. 77 | // This function is part of the rlz_lib namespace so that it can be called from 78 | // unit tests. Non-unit test code should not call this function. 79 | bool HasAccess(PSID sid, ACCESS_MASK access_mask, ACL* dacl) { 80 | if (dacl == NULL) 81 | return false; 82 | 83 | ACL_SIZE_INFORMATION info; 84 | if (!GetAclInformation(dacl, &info, sizeof(info), AclSizeInformation)) 85 | return false; 86 | 87 | GENERIC_MAPPING generic_mapping = {KEY_READ, KEY_WRITE, KEY_EXECUTE, 88 | KEY_ALL_ACCESS}; 89 | MapGenericMask(&access_mask, &generic_mapping); 90 | 91 | for (DWORD i = 0; i < info.AceCount; ++i) { 92 | ACCESS_ALLOWED_ACE* ace; 93 | if (GetAce(dacl, i, reinterpret_cast(&ace))) { 94 | if ((ace->Header.AceFlags & INHERIT_ONLY_ACE) == INHERIT_ONLY_ACE) 95 | continue; 96 | 97 | PSID existing_sid = reinterpret_cast(&ace->SidStart); 98 | DWORD mask = ace->Mask; 99 | MapGenericMask(&mask, &generic_mapping); 100 | 101 | if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE && 102 | (mask & access_mask) == access_mask && EqualSid(existing_sid, sid)) 103 | return true; 104 | 105 | if (ace->Header.AceType == ACCESS_DENIED_ACE_TYPE && 106 | (mask & access_mask) != 0 && EqualSid(existing_sid, sid)) 107 | return false; 108 | } 109 | } 110 | 111 | return false; 112 | } 113 | 114 | bool CreateMachineState() { 115 | LibMutex lock; 116 | if (lock.failed()) 117 | return false; 118 | 119 | base::win::RegKey hklm_key; 120 | if (hklm_key.Create(HKEY_LOCAL_MACHINE, 121 | RlzValueStoreRegistry::GetWideLibKeyName().c_str(), 122 | KEY_ALL_ACCESS | KEY_WOW64_32KEY) != ERROR_SUCCESS) { 123 | ASSERT_STRING("rlz_lib::CreateMachineState: " 124 | "Unable to create / open machine key."); 125 | return false; 126 | } 127 | 128 | // Create a SID that represents ALL USERS. 129 | DWORD users_sid_size = SECURITY_MAX_SID_SIZE; 130 | typed_buffer_ptr users_sid(users_sid_size); 131 | CreateWellKnownSid(WinBuiltinUsersSid, NULL, users_sid, &users_sid_size); 132 | 133 | // Get the security descriptor for the registry key. 134 | DWORD original_sd_size = 0; 135 | ::RegGetKeySecurity(hklm_key.Handle(), DACL_SECURITY_INFORMATION, NULL, 136 | &original_sd_size); 137 | typed_buffer_ptr original_sd(original_sd_size); 138 | 139 | LONG result = ::RegGetKeySecurity(hklm_key.Handle(), 140 | DACL_SECURITY_INFORMATION, original_sd, &original_sd_size); 141 | if (result != ERROR_SUCCESS) { 142 | ASSERT_STRING("rlz_lib::CreateMachineState: " 143 | "Unable to create / open machine key."); 144 | return false; 145 | } 146 | 147 | // Make a copy of the security descriptor so we can modify it. The one 148 | // returned by RegGetKeySecurity() is self-relative, so we need to make it 149 | // absolute. 150 | DWORD new_sd_size = 0; 151 | DWORD dacl_size = 0; 152 | DWORD sacl_size = 0; 153 | DWORD owner_size = 0; 154 | DWORD group_size = 0; 155 | ::MakeAbsoluteSD(original_sd, NULL, &new_sd_size, NULL, &dacl_size, 156 | NULL, &sacl_size, NULL, &owner_size, 157 | NULL, &group_size); 158 | 159 | typed_buffer_ptr new_sd(new_sd_size); 160 | // Make sure the DACL is big enough to add one more ACE. 161 | typed_buffer_ptr dacl(dacl_size + SECURITY_MAX_SID_SIZE); 162 | typed_buffer_ptr sacl(sacl_size); 163 | typed_buffer_ptr owner(owner_size); 164 | typed_buffer_ptr group(group_size); 165 | 166 | if (!::MakeAbsoluteSD(original_sd, new_sd, &new_sd_size, dacl, &dacl_size, 167 | sacl, &sacl_size, owner, &owner_size, 168 | group, &group_size)) { 169 | ASSERT_STRING("rlz_lib::CreateMachineState: MakeAbsoluteSD failed"); 170 | return false; 171 | } 172 | 173 | // If all users already have read/write access to the registry key, then 174 | // nothing to do. Otherwise change the security descriptor of the key to 175 | // give everyone access. 176 | if (HasAccess(users_sid, KEY_ALL_ACCESS, dacl)) { 177 | return false; 178 | } 179 | 180 | // Add ALL-USERS ALL-ACCESS ACL. 181 | EXPLICIT_ACCESS ea; 182 | ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS)); 183 | ea.grfAccessPermissions = GENERIC_ALL | KEY_ALL_ACCESS; 184 | ea.grfAccessMode = GRANT_ACCESS; 185 | ea.grfInheritance= SUB_CONTAINERS_AND_OBJECTS_INHERIT; 186 | ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME; 187 | ea.Trustee.ptstrName = L"Everyone"; 188 | 189 | ACL* new_dacl = NULL; 190 | result = SetEntriesInAcl(1, &ea, dacl, &new_dacl); 191 | if (result != ERROR_SUCCESS) { 192 | ASSERT_STRING("rlz_lib::CreateMachineState: SetEntriesInAcl failed"); 193 | return false; 194 | } 195 | 196 | BOOL ok = SetSecurityDescriptorDacl(new_sd, TRUE, new_dacl, FALSE); 197 | if (!ok) { 198 | ASSERT_STRING("rlz_lib::CreateMachineState: " 199 | "SetSecurityDescriptorOwner failed"); 200 | LocalFree(new_dacl); 201 | return false; 202 | } 203 | 204 | result = ::RegSetKeySecurity(hklm_key.Handle(), 205 | DACL_SECURITY_INFORMATION, 206 | new_sd); 207 | // Note that the new DACL cannot be freed until after the call to 208 | // RegSetKeySecurity(). 209 | LocalFree(new_dacl); 210 | 211 | bool success = true; 212 | if (result != ERROR_SUCCESS) { 213 | ASSERT_STRING("rlz_lib::CreateMachineState: " 214 | "Unable to create / open machine key."); 215 | success = false; 216 | } 217 | 218 | 219 | return success; 220 | } 221 | 222 | bool SetMachineDealCode(const char* dcc) { 223 | return MachineDealCode::Set(dcc); 224 | } 225 | 226 | bool GetMachineDealCodeAsCgi(char* cgi, size_t cgi_size) { 227 | return MachineDealCode::GetAsCgi(cgi, cgi_size); 228 | } 229 | 230 | bool GetMachineDealCode(char* dcc, size_t dcc_size) { 231 | return MachineDealCode::Get(dcc, dcc_size); 232 | } 233 | 234 | // Combined functions. 235 | 236 | bool SetMachineDealCodeFromPingResponse(const char* response) { 237 | return MachineDealCode::SetFromPingResponse(response); 238 | } 239 | 240 | void InitializeTempHivesForTesting(const base::win::RegKey& temp_hklm_key, 241 | const base::win::RegKey& temp_hkcu_key) { 242 | // For the moment, the HKCU hive requires no initialization. 243 | 244 | if (base::win::GetVersion() >= base::win::VERSION_WIN7) { 245 | // Copy the following HKLM subtrees to the temporary location so that the 246 | // win32 APIs used by the tests continue to work: 247 | // 248 | // HKLM\System\CurrentControlSet\Control\Lsa\AccessProviders 249 | // 250 | // This seems to be required since Win7. 251 | base::win::RegKey dest(temp_hklm_key.Handle(), kHKLMAccessProviders, 252 | KEY_ALL_ACCESS); 253 | CopyRegistryTree(base::win::RegKey(HKEY_LOCAL_MACHINE, 254 | kHKLMAccessProviders, 255 | KEY_READ), 256 | &dest); 257 | } 258 | } 259 | 260 | } // namespace rlz_lib 261 | -------------------------------------------------------------------------------- /win/lib/rlz_value_store_registry.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | 5 | #include "rlz/win/lib/rlz_value_store_registry.h" 6 | 7 | #include "base/win/registry.h" 8 | #include "base/stringprintf.h" 9 | #include "base/utf_string_conversions.h" 10 | #include "rlz/lib/assert.h" 11 | #include "rlz/lib/lib_values.h" 12 | #include "rlz/lib/rlz_lib.h" 13 | #include "rlz/lib/string_utils.h" 14 | #include "rlz/win/lib/registry_util.h" 15 | 16 | namespace rlz_lib { 17 | 18 | namespace { 19 | 20 | // 21 | // Registry keys: 22 | // 23 | // RLZ's are stored as: 24 | // = @ kRootKey\kLibKeyName\kRlzsSubkeyName. 25 | // 26 | // Events are stored as: 27 | // = 1 @ 28 | // HKCU\kLibKeyName\kEventsSubkeyName\GetProductName(product). 29 | // 30 | // The OEM Deal Confirmation Code (DCC) is stored as 31 | // kDccValueName = @ HKLM\kLibKeyName 32 | // 33 | // The last ping time, per product is stored as: 34 | // GetProductName(product) = @ 35 | // HKCU\kLibKeyName\kPingTimesSubkeyName. 36 | // 37 | // The server does not care about any of these constants. 38 | // 39 | const char kLibKeyName[] = "Software\\Google\\Common\\Rlz"; 40 | const wchar_t kGoogleKeyName[] = L"Software\\Google"; 41 | const wchar_t kGoogleCommonKeyName[] = L"Software\\Google\\Common"; 42 | const char kRlzsSubkeyName[] = "RLZs"; 43 | const char kEventsSubkeyName[] = "Events"; 44 | const char kStatefulEventsSubkeyName[] = "StatefulEvents"; 45 | const char kPingTimesSubkeyName[] = "PTimes"; 46 | 47 | std::wstring GetWideProductName(Product product) { 48 | return ASCIIToWide(GetProductName(product)); 49 | } 50 | 51 | void AppendBrandToString(std::string* str) { 52 | std::string brand(SupplementaryBranding::GetBrand()); 53 | if (!brand.empty()) 54 | base::StringAppendF(str, "\\_%s", brand.c_str()); 55 | } 56 | 57 | // Function to get the specific registry keys. 58 | bool GetRegKey(const char* name, REGSAM access, base::win::RegKey* key) { 59 | std::string key_location; 60 | base::StringAppendF(&key_location, "%s\\%s", kLibKeyName, name); 61 | AppendBrandToString(&key_location); 62 | 63 | LONG ret = ERROR_SUCCESS; 64 | if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK)) { 65 | ret = key->Create(HKEY_CURRENT_USER, ASCIIToWide(key_location).c_str(), 66 | access); 67 | } else { 68 | ret = key->Open(HKEY_CURRENT_USER, ASCIIToWide(key_location).c_str(), 69 | access); 70 | } 71 | 72 | return ret == ERROR_SUCCESS; 73 | } 74 | 75 | bool GetPingTimesRegKey(REGSAM access, base::win::RegKey* key) { 76 | return GetRegKey(kPingTimesSubkeyName, access, key); 77 | } 78 | 79 | 80 | bool GetEventsRegKey(const char* event_type, 81 | const rlz_lib::Product* product, 82 | REGSAM access, base::win::RegKey* key) { 83 | std::string key_location; 84 | base::StringAppendF(&key_location, "%s\\%s", kLibKeyName, 85 | event_type); 86 | AppendBrandToString(&key_location); 87 | 88 | if (product != NULL) { 89 | std::string product_name = GetProductName(*product); 90 | if (product_name.empty()) 91 | return false; 92 | 93 | base::StringAppendF(&key_location, "\\%s", product_name.c_str()); 94 | } 95 | 96 | LONG ret = ERROR_SUCCESS; 97 | if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK)) { 98 | ret = key->Create(HKEY_CURRENT_USER, ASCIIToWide(key_location).c_str(), 99 | access); 100 | } else { 101 | ret = key->Open(HKEY_CURRENT_USER, ASCIIToWide(key_location).c_str(), 102 | access); 103 | } 104 | 105 | return ret == ERROR_SUCCESS; 106 | } 107 | 108 | bool GetAccessPointRlzsRegKey(REGSAM access, base::win::RegKey* key) { 109 | return GetRegKey(kRlzsSubkeyName, access, key); 110 | } 111 | 112 | bool ClearAllProductEventValues(rlz_lib::Product product, const char* key) { 113 | std::wstring product_name = GetWideProductName(product); 114 | if (product_name.empty()) 115 | return false; 116 | 117 | base::win::RegKey reg_key; 118 | GetEventsRegKey(key, NULL, KEY_WRITE, ®_key); 119 | reg_key.DeleteKey(product_name.c_str()); 120 | 121 | // Verify that the value no longer exists. 122 | base::win::RegKey product_events( 123 | reg_key.Handle(), product_name.c_str(), KEY_READ); 124 | if (product_events.Valid()) { 125 | ASSERT_STRING("ClearAllProductEvents: Key deletion failed"); 126 | return false; 127 | } 128 | 129 | return true; 130 | } 131 | 132 | // Deletes a registry key if it exists and has no subkeys or values. 133 | // TODO: Move this to a registry_utils file and add unittest. 134 | bool DeleteKeyIfEmpty(HKEY root_key, const wchar_t* key_name) { 135 | if (!key_name) { 136 | ASSERT_STRING("DeleteKeyIfEmpty: key_name is NULL"); 137 | return false; 138 | } else { // Scope needed for RegKey 139 | base::win::RegKey key(root_key, key_name, KEY_READ); 140 | if (!key.Valid()) 141 | return true; // Key does not exist - nothing to do. 142 | 143 | base::win::RegistryKeyIterator key_iter(root_key, key_name); 144 | if (key_iter.SubkeyCount() > 0) 145 | return true; // Not empty, so nothing to do 146 | 147 | base::win::RegistryValueIterator value_iter(root_key, key_name); 148 | if (value_iter.ValueCount() > 0) 149 | return true; // Not empty, so nothing to do 150 | } 151 | 152 | // The key is empty - delete it now. 153 | base::win::RegKey key(root_key, L"", KEY_WRITE); 154 | return key.DeleteKey(key_name) == ERROR_SUCCESS; 155 | } 156 | 157 | } // namespace 158 | 159 | // static 160 | std::wstring RlzValueStoreRegistry::GetWideLibKeyName() { 161 | return ASCIIToWide(kLibKeyName); 162 | } 163 | 164 | bool RlzValueStoreRegistry::HasAccess(AccessType type) { 165 | return HasUserKeyAccess(type == kWriteAccess); 166 | } 167 | 168 | bool RlzValueStoreRegistry::WritePingTime(Product product, int64 time) { 169 | base::win::RegKey key; 170 | std::wstring product_name = GetWideProductName(product); 171 | return GetPingTimesRegKey(KEY_WRITE, &key) && 172 | key.WriteValue(product_name.c_str(), &time, sizeof(time), 173 | REG_QWORD) == ERROR_SUCCESS; 174 | } 175 | 176 | bool RlzValueStoreRegistry::ReadPingTime(Product product, int64* time) { 177 | base::win::RegKey key; 178 | std::wstring product_name = GetWideProductName(product); 179 | return GetPingTimesRegKey(KEY_READ, &key) && 180 | key.ReadInt64(product_name.c_str(), time) == ERROR_SUCCESS; 181 | } 182 | 183 | bool RlzValueStoreRegistry::ClearPingTime(Product product) { 184 | base::win::RegKey key; 185 | GetPingTimesRegKey(KEY_WRITE, &key); 186 | 187 | std::wstring product_name = GetWideProductName(product); 188 | key.DeleteValue(product_name.c_str()); 189 | 190 | // Verify deletion. 191 | uint64 value; 192 | DWORD size = sizeof(value); 193 | if (key.ReadValue( 194 | product_name.c_str(), &value, &size, NULL) == ERROR_SUCCESS) { 195 | ASSERT_STRING("RlzValueStoreRegistry::ClearPingTime: Failed to delete."); 196 | return false; 197 | } 198 | 199 | return true; 200 | } 201 | 202 | bool RlzValueStoreRegistry::WriteAccessPointRlz(AccessPoint access_point, 203 | const char* new_rlz) { 204 | const char* access_point_name = GetAccessPointName(access_point); 205 | if (!access_point_name) 206 | return false; 207 | 208 | std::wstring access_point_name_wide(ASCIIToWide(access_point_name)); 209 | base::win::RegKey key; 210 | GetAccessPointRlzsRegKey(KEY_WRITE, &key); 211 | 212 | if (!RegKeyWriteValue(key, access_point_name_wide.c_str(), new_rlz)) { 213 | ASSERT_STRING("SetAccessPointRlz: Could not write the new RLZ value"); 214 | return false; 215 | } 216 | return true; 217 | } 218 | 219 | bool RlzValueStoreRegistry::ReadAccessPointRlz(AccessPoint access_point, 220 | char* rlz, 221 | size_t rlz_size) { 222 | const char* access_point_name = GetAccessPointName(access_point); 223 | if (!access_point_name) 224 | return false; 225 | 226 | size_t size = rlz_size; 227 | base::win::RegKey key; 228 | GetAccessPointRlzsRegKey(KEY_READ, &key); 229 | if (!RegKeyReadValue(key, ASCIIToWide(access_point_name).c_str(), 230 | rlz, &size)) { 231 | rlz[0] = 0; 232 | if (size > rlz_size) { 233 | ASSERT_STRING("GetAccessPointRlz: Insufficient buffer size"); 234 | return false; 235 | } 236 | } 237 | return true; 238 | } 239 | 240 | bool RlzValueStoreRegistry::ClearAccessPointRlz(AccessPoint access_point) { 241 | const char* access_point_name = GetAccessPointName(access_point); 242 | if (!access_point_name) 243 | return false; 244 | 245 | std::wstring access_point_name_wide(ASCIIToWide(access_point_name)); 246 | base::win::RegKey key; 247 | GetAccessPointRlzsRegKey(KEY_WRITE, &key); 248 | 249 | key.DeleteValue(access_point_name_wide.c_str()); 250 | 251 | // Verify deletion. 252 | DWORD value; 253 | if (key.ReadValueDW(access_point_name_wide.c_str(), &value) == 254 | ERROR_SUCCESS) { 255 | ASSERT_STRING("SetAccessPointRlz: Could not clear the RLZ value."); 256 | return false; 257 | } 258 | return true; 259 | } 260 | 261 | bool RlzValueStoreRegistry::AddProductEvent(Product product, 262 | const char* event_rlz) { 263 | std::wstring event_rlz_wide(ASCIIToWide(event_rlz)); 264 | base::win::RegKey reg_key; 265 | GetEventsRegKey(kEventsSubkeyName, &product, KEY_WRITE, ®_key); 266 | if (reg_key.WriteValue(event_rlz_wide.c_str(), 1) != ERROR_SUCCESS) { 267 | ASSERT_STRING("AddProductEvent: Could not write the new event value"); 268 | return false; 269 | } 270 | 271 | return true; 272 | } 273 | 274 | bool RlzValueStoreRegistry::ReadProductEvents(Product product, 275 | std::vector* events) { 276 | // Open the events key. 277 | base::win::RegKey events_key; 278 | GetEventsRegKey(kEventsSubkeyName, &product, KEY_READ, &events_key); 279 | if (!events_key.Valid()) 280 | return false; 281 | 282 | // Append the events to the buffer. 283 | int num_values = 0; 284 | LONG result = ERROR_SUCCESS; 285 | for (num_values = 0; result == ERROR_SUCCESS; ++num_values) { 286 | // Max 32767 bytes according to MSDN, but we never use that much. 287 | const size_t kMaxValueNameLength = 2048; 288 | char buffer[kMaxValueNameLength]; 289 | DWORD size = arraysize(buffer); 290 | 291 | result = RegEnumValueA(events_key.Handle(), num_values, buffer, &size, 292 | NULL, NULL, NULL, NULL); 293 | if (result == ERROR_SUCCESS) 294 | events->push_back(std::string(buffer)); 295 | } 296 | 297 | return result == ERROR_NO_MORE_ITEMS; 298 | } 299 | 300 | bool RlzValueStoreRegistry::ClearProductEvent(Product product, 301 | const char* event_rlz) { 302 | std::wstring event_rlz_wide(ASCIIToWide(event_rlz)); 303 | base::win::RegKey key; 304 | GetEventsRegKey(kEventsSubkeyName, &product, KEY_WRITE, &key); 305 | key.DeleteValue(event_rlz_wide.c_str()); 306 | 307 | // Verify deletion. 308 | DWORD value; 309 | if (key.ReadValueDW(event_rlz_wide.c_str(), &value) == ERROR_SUCCESS) { 310 | ASSERT_STRING("ClearProductEvent: Could not delete the event value."); 311 | return false; 312 | } 313 | 314 | return true; 315 | } 316 | 317 | bool RlzValueStoreRegistry::ClearAllProductEvents(Product product) { 318 | return ClearAllProductEventValues(product, kEventsSubkeyName); 319 | } 320 | 321 | bool RlzValueStoreRegistry::AddStatefulEvent(Product product, 322 | const char* event_rlz) { 323 | base::win::RegKey key; 324 | std::wstring event_rlz_wide(ASCIIToWide(event_rlz)); 325 | if (!GetEventsRegKey(kStatefulEventsSubkeyName, &product, KEY_WRITE, &key) || 326 | key.WriteValue(event_rlz_wide.c_str(), 1) != ERROR_SUCCESS) { 327 | ASSERT_STRING( 328 | "AddStatefulEvent: Could not write the new stateful event"); 329 | return false; 330 | } 331 | 332 | return true; 333 | } 334 | 335 | bool RlzValueStoreRegistry::IsStatefulEvent(Product product, 336 | const char* event_rlz) { 337 | DWORD value; 338 | base::win::RegKey key; 339 | GetEventsRegKey(kStatefulEventsSubkeyName, &product, KEY_READ, &key); 340 | std::wstring event_rlz_wide(ASCIIToWide(event_rlz)); 341 | return key.ReadValueDW(event_rlz_wide.c_str(), &value) == ERROR_SUCCESS; 342 | } 343 | 344 | bool RlzValueStoreRegistry::ClearAllStatefulEvents(Product product) { 345 | return ClearAllProductEventValues(product, kStatefulEventsSubkeyName); 346 | } 347 | 348 | void RlzValueStoreRegistry::CollectGarbage() { 349 | // Delete each of the known subkeys if empty. 350 | const char* subkeys[] = { 351 | kRlzsSubkeyName, 352 | kEventsSubkeyName, 353 | kStatefulEventsSubkeyName, 354 | kPingTimesSubkeyName 355 | }; 356 | 357 | for (int i = 0; i < arraysize(subkeys); i++) { 358 | std::string subkey_name; 359 | base::StringAppendF(&subkey_name, "%s\\%s", kLibKeyName, subkeys[i]); 360 | AppendBrandToString(&subkey_name); 361 | 362 | VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER, 363 | ASCIIToWide(subkey_name).c_str())); 364 | } 365 | 366 | // Delete the library key and its parents too now if empty. 367 | VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER, GetWideLibKeyName().c_str())); 368 | VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER, kGoogleCommonKeyName)); 369 | VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER, kGoogleKeyName)); 370 | } 371 | 372 | ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() { 373 | if (!lock_.failed()) 374 | store_.reset(new RlzValueStoreRegistry); 375 | } 376 | 377 | ScopedRlzValueStoreLock::~ScopedRlzValueStoreLock() { 378 | } 379 | 380 | RlzValueStore* ScopedRlzValueStoreLock::GetStore() { 381 | return store_.get(); 382 | } 383 | 384 | } // namespace rlz_lib 385 | -------------------------------------------------------------------------------- /win/lib/rlz_value_store_registry.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | 5 | #ifndef RLZ_WIN_LIB_RLZ_VALUE_STORE_REGISTRY_H_ 6 | #define RLZ_WIN_LIB_RLZ_VALUE_STORE_REGISTRY_H_ 7 | 8 | #include "base/compiler_specific.h" 9 | #include "rlz/lib/rlz_value_store.h" 10 | 11 | namespace rlz_lib { 12 | 13 | // Implements RlzValueStore by storing values in the windows registry. 14 | class RlzValueStoreRegistry : public RlzValueStore { 15 | public: 16 | static std::wstring GetWideLibKeyName(); 17 | 18 | virtual bool HasAccess(AccessType type) OVERRIDE; 19 | 20 | virtual bool WritePingTime(Product product, int64 time) OVERRIDE; 21 | virtual bool ReadPingTime(Product product, int64* time) OVERRIDE; 22 | virtual bool ClearPingTime(Product product) OVERRIDE; 23 | 24 | virtual bool WriteAccessPointRlz(AccessPoint access_point, 25 | const char* new_rlz) OVERRIDE; 26 | virtual bool ReadAccessPointRlz(AccessPoint access_point, 27 | char* rlz, 28 | size_t rlz_size) OVERRIDE; 29 | virtual bool ClearAccessPointRlz(AccessPoint access_point) OVERRIDE; 30 | 31 | virtual bool AddProductEvent(Product product, const char* event_rlz) OVERRIDE; 32 | virtual bool ReadProductEvents(Product product, 33 | std::vector* events) OVERRIDE; 34 | virtual bool ClearProductEvent(Product product, 35 | const char* event_rlz) OVERRIDE; 36 | virtual bool ClearAllProductEvents(Product product) OVERRIDE; 37 | 38 | virtual bool AddStatefulEvent(Product product, 39 | const char* event_rlz) OVERRIDE; 40 | virtual bool IsStatefulEvent(Product product, 41 | const char* event_rlz) OVERRIDE; 42 | virtual bool ClearAllStatefulEvents(Product product) OVERRIDE; 43 | 44 | virtual void CollectGarbage() OVERRIDE; 45 | 46 | private: 47 | RlzValueStoreRegistry() {} 48 | DISALLOW_COPY_AND_ASSIGN(RlzValueStoreRegistry); 49 | friend class ScopedRlzValueStoreLock; 50 | }; 51 | 52 | } // namespace rlz_lib 53 | 54 | #endif // RLZ_WIN_LIB_RLZ_VALUE_STORE_REGISTRY_H_ 55 | 56 | -------------------------------------------------------------------------------- /win/lib/vista_winnt.h: -------------------------------------------------------------------------------- 1 | // Copyright 2010 Google Inc. All Rights Reserved. 2 | // Use of this source code is governed by an Apache-style license that can be 3 | // found in the COPYING file. 4 | // 5 | // This file contains snippets borrowed from the Vista SDK version of 6 | // WinNT.h, (c) Microsoft (2006) 7 | 8 | #ifndef RLZ_WIN_LIB_VISTA_WINNT_H_ 9 | #define RLZ_WIN_LIB_VISTA_WINNT_H_ 10 | 11 | #include 12 | 13 | // If no Vista SDK yet, borrow these from Vista's version of WinNT.h 14 | #ifndef SE_GROUP_INTEGRITY 15 | 16 | // TOKEN_MANDATORY_LABEL.Label.Attributes = SE_GROUP_INTEGRITY 17 | #define SE_GROUP_INTEGRITY (0x00000020L) 18 | #define SE_GROUP_INTEGRITY_ENABLED (0x00000040L) 19 | 20 | typedef struct _TOKEN_MANDATORY_LABEL { 21 | SID_AND_ATTRIBUTES Label; 22 | } TOKEN_MANDATORY_LABEL, *PTOKEN_MANDATORY_LABEL; 23 | 24 | // These are a few new enums for TOKEN_INFORMATION_CLASS 25 | #define TokenElevationType static_cast(18) 26 | #define TokenLinkedToken static_cast(19) 27 | #define TokenElevation static_cast(20) 28 | #define TokenHasRestrictions static_cast(21) 29 | #define TokenAccessInformation static_cast(22) 30 | #define TokenVirtualizationAllowed static_cast(23) 31 | #define TokenVirtualizationEnabled static_cast(24) 32 | // TokenIntegrityLevel is the proces's privilege level, low, med, or high 33 | #define TokenIntegrityLevel static_cast(25) 34 | // TokenIntegrityLevelDeasktop is an alternate level used for access apis 35 | // (screen readers, imes) 36 | #define TokenIntegrityLevelDesktop static_cast(26) 37 | 38 | // This is a new flag to pass to GetNamedSecurityInfo or SetNamedSecurityInfo 39 | // that puts the mandatory level label info in an access control list (ACL) 40 | // structure in the parameter normally used for system acls (SACL) 41 | #define LABEL_SECURITY_INFORMATION (0x00000010L) 42 | 43 | // The new Access Control Entry type identifier for mandatory labels 44 | #define SYSTEM_MANDATORY_LABEL_ACE_TYPE (0x11) 45 | 46 | // The structure of mandatory label acess control binary entry 47 | typedef struct _SYSTEM_MANDATORY_LABEL_ACE { 48 | ACE_HEADER Header; 49 | ACCESS_MASK Mask; 50 | DWORD SidStart; 51 | } SYSTEM_MANDATORY_LABEL_ACE, *PSYSTEM_MANDATORY_LABEL_ACE; 52 | 53 | // Masks for ACCESS_MASK above 54 | #define SYSTEM_MANDATORY_LABEL_NO_WRITE_UP 0x1 55 | #define SYSTEM_MANDATORY_LABEL_NO_READ_UP 0x2 56 | #define SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP 0x4 57 | #define SYSTEM_MANDATORY_LABEL_VALID_MASK \ 58 | (SYSTEM_MANDATORY_LABEL_NO_WRITE_UP | \ 59 | SYSTEM_MANDATORY_LABEL_NO_READ_UP | \ 60 | SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP) 61 | 62 | // The SID authority for mandatory labels 63 | #define SECURITY_MANDATORY_LABEL_AUTHORITY {0, 0, 0, 0, 0, 16} 64 | 65 | // the RID values (sub authorities) that define mandatory label levels 66 | #define SECURITY_MANDATORY_UNTRUSTED_RID (0x00000000L) 67 | #define SECURITY_MANDATORY_LOW_RID (0x00001000L) 68 | #define SECURITY_MANDATORY_MEDIUM_RID (0x00002000L) 69 | #define SECURITY_MANDATORY_HIGH_RID (0x00003000L) 70 | #define SECURITY_MANDATORY_SYSTEM_RID (0x00004000L) 71 | #define SECURITY_MANDATORY_UI_ACCESS_RID (0x00004100L) 72 | #define SECURITY_MANDATORY_PROTECTED_PROCESS_RID (0x00005000L) 73 | 74 | // Vista's mandatory labels, enumerated 75 | typedef enum _MANDATORY_LEVEL { 76 | MandatoryLevelUntrusted = 0, 77 | MandatoryLevelLow, 78 | MandatoryLevelMedium, 79 | MandatoryLevelHigh, 80 | MandatoryLevelSystem, 81 | MandatoryLevelSecureProcess, 82 | MandatoryLevelCount 83 | } MANDATORY_LEVEL, *PMANDATORY_LEVEL; 84 | 85 | 86 | // Token elevation values describe the relative strength of a given token. 87 | // A full token is a token with all groups and privileges to which the 88 | // principal is authorized. A limited token is one with some groups or 89 | // privileges removed. 90 | 91 | typedef enum _TOKEN_ELEVATION_TYPE { 92 | TokenElevationTypeDefault = 1, 93 | TokenElevationTypeFull, 94 | TokenElevationTypeLimited, 95 | } TOKEN_ELEVATION_TYPE, *PTOKEN_ELEVATION_TYPE; 96 | 97 | #endif // #ifndef SE_GROUP_INTEGRITY 98 | 99 | #endif // RLZ_WIN_LIB_VISTA_WINNT_H_ 100 | --------------------------------------------------------------------------------