0

Revert 144071 - Add a regenerate button to regenerate the password in Windows.

BUG=120480
TEST=Not tested.


Review URL: https://chromiumcodereview.appspot.com/10642009

TBR=zysxqn@google.com
Review URL: https://chromiumcodereview.appspot.com/10659022

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@144074 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
jam@chromium.org
2012-06-26 00:39:45 +00:00
parent 4a44f6610e
commit 6dba1dd2ef
55 changed files with 6986 additions and 34 deletions

@ -2487,8 +2487,6 @@ void BrowserView::ShowPasswordGenerationBubble(
const gfx::Rect& rect,
autofill::PasswordGenerator* password_generator,
const webkit::forms::PasswordForm& form) {
ui::ThemeProvider* theme_provider = GetWidget()->GetThemeProvider();
// Create a rect in the content bounds that the bubble will point to.
gfx::Point origin(rect.origin());
views::View::ConvertPointToScreen(GetTabContentsContainerView(), &origin);
@ -2507,8 +2505,7 @@ void BrowserView::ShowPasswordGenerationBubble(
tab_contents->web_contents()->GetRenderViewHost(),
password_generator,
browser_.get(),
tab_contents->password_manager(),
theme_provider);
tab_contents->password_manager());
views::BubbleDelegateView::CreateBubble(bubble);
bubble->SetAlignment(views::BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR);

@ -15,10 +15,7 @@
#include "content/public/browser/render_view_host.h"
#include "googleurl/src/gurl.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources_standard.h"
#include "ui/base/theme_provider.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/button/text_button.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/link.h"
@ -36,19 +33,16 @@ PasswordGenerationBubbleView::PasswordGenerationBubbleView(
content::RenderViewHost* render_view_host,
autofill::PasswordGenerator* password_generator,
content::PageNavigator* navigator,
PasswordManager* password_manager,
ui::ThemeProvider* theme_provider)
PasswordManager* password_manager)
: BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_LEFT),
accept_button_(NULL),
regenerate_button_(NULL),
text_field_(NULL),
anchor_rect_(anchor_rect),
form_(form),
render_view_host_(render_view_host),
password_generator_(password_generator),
navigator_(navigator),
password_manager_(password_manager),
theme_provider_(theme_provider) {}
password_manager_(password_manager) {}
PasswordGenerationBubbleView::~PasswordGenerationBubbleView() {}
@ -58,15 +52,6 @@ void PasswordGenerationBubbleView::Init() {
accept_button_ = new views::NativeTextButton(this,
ASCIIToUTF16("Try It"));
regenerate_button_ = new views::ImageButton(this);
regenerate_button_->SetImage(views::CustomButton::BS_NORMAL,
theme_provider_->GetImageSkiaNamed(IDR_RELOAD));
regenerate_button_->SetImage(views::CustomButton::BS_HOT,
theme_provider_->GetImageSkiaNamed(IDR_RELOAD_H));
regenerate_button_->SetImage(views::CustomButton::BS_PUSHED,
theme_provider_->GetImageSkiaNamed(IDR_RELOAD_P));
regenerate_button_->SetTooltipText(ASCIIToUTF16("Regenerate"));
text_field_ = new views::Textfield();
text_field_->SetText(
ASCIIToUTF16(password_generator_->Generate()));
@ -94,8 +79,6 @@ void PasswordGenerationBubbleView::Init() {
cs = layout->AddColumnSet(1);
cs->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0,
GridLayout::USE_PREF, 0, 100);
cs->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0, GridLayout::FIXED,
regenerate_button_->GetPreferredSize().width(), 100);
cs->AddPaddingColumn(1, views::kRelatedControlHorizontalSpacing);
cs->AddColumn(GridLayout::TRAILING, GridLayout::CENTER, 0,
GridLayout::USE_PREF, 0, 0);
@ -106,7 +89,6 @@ void PasswordGenerationBubbleView::Init() {
layout->StartRow(0, 1);
layout->AddView(text_field_);
layout->AddView(regenerate_button_);
layout->AddView(accept_button_);
}
@ -121,9 +103,6 @@ void PasswordGenerationBubbleView::ButtonPressed(views::Button* sender,
render_view_host_->GetRoutingID(), text_field_->text()));
password_manager_->SetFormHasGeneratedPassword(form_);
StartFade(false);
} else if (sender == regenerate_button_) {
text_field_->SetText(
ASCIIToUTF16(password_generator_->Generate()));
}
}

@ -24,7 +24,6 @@ class RenderViewHost;
}
namespace views {
class ImageButton;
class TextButton;
class Textfield;
}
@ -45,8 +44,7 @@ class PasswordGenerationBubbleView : public views::BubbleDelegateView,
content::RenderViewHost* render_view_host,
autofill::PasswordGenerator* password_generator,
content::PageNavigator* navigator,
PasswordManager* password_manager,
ui::ThemeProvider* theme_provider);
PasswordManager* password_manager);
virtual ~PasswordGenerationBubbleView();
private:
@ -66,7 +64,6 @@ class PasswordGenerationBubbleView : public views::BubbleDelegateView,
// Subviews
views::TextButton* accept_button_;
views::ImageButton* regenerate_button_;
views::Textfield* text_field_;
// Location that the bubble points to
@ -88,9 +85,6 @@ class PasswordGenerationBubbleView : public views::BubbleDelegateView,
// PasswordManager associated with this tab.
PasswordManager* password_manager_;
// Theme provider used to draw the regenerate button.
ui::ThemeProvider* theme_provider_;
DISALLOW_COPY_AND_ASSIGN(PasswordGenerationBubbleView);
};

70
rlz/DEPS Normal file

@ -0,0 +1,70 @@
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# This DEPS file exists so that it can capture the dependenciez of RLZ. This
# allows external projects to check it out and build it independently from all
# of chrome.
vars = {
"chrev": "@119173"
}
deps = {
"src/base":
"http://src.chromium.org/svn/trunk/src/base" + Var("chrev"),
"src/build":
"http://src.chromium.org/svn/trunk/src/build" + Var("chrev"),
"src/third_party/icu":
"http://src.chromium.org/svn/trunk/deps/third_party/icu42" + Var("chrev"),
"src/third_party/modp_b64":
"http://src.chromium.org/svn/trunk/src/third_party/modp_b64" + Var("chrev"),
"src/third_party/nss":
"http://src.chromium.org/svn/trunk/deps/third_party/nss" + Var("chrev"),
"src/third_party/sqlite":
"http://src.chromium.org/svn/trunk/src/third_party/sqlite" + Var("chrev"),
"src/third_party/wtl":
"http://src.chromium.org/svn/trunk/src/third_party/wtl" + Var("chrev"),
"src/third_party/zlib":
"http://src.chromium.org/svn/trunk/src/third_party/zlib" + Var("chrev"),
"src/testing":
"http://src.chromium.org/svn/trunk/src/testing" + Var("chrev"),
"src/testing/gmock":
"http://googlemock.googlecode.com/svn/trunk@374",
"src/testing/gtest":
"http://googletest.googlecode.com/svn/trunk@492",
"src/tools/gyp":
"http://gyp.googlecode.com/svn/trunk@1233",
"src/tools/win":
"http://src.chromium.org/svn/trunk/src/tools/win" + Var("chrev"),
# If using rlz with chrome's networking library, add it and its dependencies
# here.
}
include_rules = [
"+build",
"+net", # This is only used when force_rlz_use_chrome_net=1 is passed to gyp.
"+third_party/zlib",
]
hooks = [
{
# A change to a .gyp, .gypi, or to GYP itself should run the generator.
"pattern": ".",
"action": ["python", "src/build/gyp_chromium", "src/rlz/rlz.gyp"],
}
]

4
rlz/OWNERS Normal file

@ -0,0 +1,4 @@
set noparent
rogerta@chromium.org
gwilson@chromium.org
thakis@chromium.org

15
rlz/lib/assert.cc Normal file

@ -0,0 +1,15 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Macros specific to the RLZ library.
#include "rlz/lib/assert.h"
namespace rlz_lib {
#ifdef MUTE_EXPECTED_ASSERTS
std::string expected_assertion_;
#endif
} // namespace rlz_lib

53
rlz/lib/assert.h Normal file

@ -0,0 +1,53 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Macros specific to the RLZ library.
#ifndef RLZ_LIB_ASSERT_H_
#define RLZ_LIB_ASSERT_H_
#include <string>
#include "base/logging.h"
// An assertion macro.
// Can mute expected assertions in debug mode.
#ifndef ASSERT_STRING
#ifndef MUTE_EXPECTED_ASSERTS
#define ASSERT_STRING(expr) LOG_IF(FATAL, false) << (expr)
#else
#define ASSERT_STRING(expr) \
do { \
std::string expr_string(expr); \
if (rlz_lib::expected_assertion_ != expr_string) { \
LOG_IF(FATAL, false) << (expr); \
} \
} while (0)
#endif
#endif
#ifndef VERIFY
#ifdef _DEBUG
#define VERIFY(expr) LOG_IF(FATAL, !(expr)) << #expr
#else
#define VERIFY(expr) (void)(expr)
#endif
#endif
namespace rlz_lib {
#ifdef MUTE_EXPECTED_ASSERTS
extern std::string expected_assertion_;
#endif
inline void SetExpectedAssertion(const char* s) {
#ifdef MUTE_EXPECTED_ASSERTS
expected_assertion_ = s;
#endif
}
} // rlz_lib
#endif // RLZ_LIB_ASSERT_H_

17
rlz/lib/crc32.h Normal file

@ -0,0 +1,17 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// A wrapper around ZLib's CRC function.
#ifndef RLZ_LIB_CRC32_H_
#define RLZ_LIB_CRC32_H_
namespace rlz_lib {
int Crc32(const unsigned char* buf, int length);
bool Crc32(const char* text, int* crc);
} // namespace rlz_lib
#endif // RLZ_LIB_CRC32_H_

52
rlz/lib/crc32_unittest.cc Normal file

@ -0,0 +1,52 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// A test for ZLib's checksum function.
#include "rlz/lib/crc32.h"
#include "base/logging.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
TEST(Crc32Unittest, ByteTest) {
struct {
const char* data;
int len;
// Externally calculated at http://crc32-checksum.waraxe.us/
int crc;
} kData[] = {
{"Hello" , 5, 0xF7D18982},
{"Google" , 6, 0x62B0F067},
{"" , 0, 0x0},
{"One more string.", 16, 0x0CA14970},
{NULL , 0, 0x0},
};
for (int i = 0; kData[i].data; i++)
EXPECT_EQ(kData[i].crc,
rlz_lib::Crc32(reinterpret_cast<const unsigned char*>(kData[i].data),
kData[i].len));
}
TEST(Crc32Unittest, CharTest) {
struct {
const char* data;
// Externally calculated at http://crc32-checksum.waraxe.us/
int crc;
} kData[] = {
{"Hello" , 0xF7D18982},
{"Google" , 0x62B0F067},
{"" , 0x0},
{"One more string.", 0x0CA14970},
{"Google\r\n" , 0x83A3E860},
{NULL , 0x0},
};
int crc;
for (int i = 0; kData[i].data; i++) {
EXPECT_TRUE(rlz_lib::Crc32(kData[i].data, &crc));
EXPECT_EQ(kData[i].crc, crc);
}
}

36
rlz/lib/crc32_wrapper.cc Normal file

@ -0,0 +1,36 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// A wrapper around ZLib's CRC functions to put them in the rlz_lib namespace
// and use our types.
#include "rlz/lib/assert.h"
#include "rlz/lib/crc32.h"
#include "rlz/lib/string_utils.h"
#include "third_party/zlib/zlib.h"
namespace rlz_lib {
int Crc32(const unsigned char* buf, int length) {
return crc32(0L, buf, length);
}
bool Crc32(const char* text, int* crc) {
if (!crc) {
ASSERT_STRING("Crc32: crc is NULL.");
return false;
}
*crc = 0;
for (int i = 0; text[i]; i++) {
if (!IsAscii(text[i]))
return false;
*crc = crc32(*crc, reinterpret_cast<const unsigned char*>(text + i), 1);
}
return true;
}
} // namespace rlz_lib

90
rlz/lib/crc8.cc Normal file

@ -0,0 +1,90 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "rlz/lib/crc8.h"
namespace {
// The CRC lookup table used for ATM HES (Polynomial = 0x07).
// These are 256 unique 8-bit values.
const unsigned char kCrcTable[256] = {
0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15,
0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5,
0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85,
0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2,
0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32,
0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C,
0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC,
0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C,
0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B,
0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB,
0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB,
0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
};
} // namespace anonymous
namespace rlz_lib {
bool Crc8::Generate(const unsigned char *data, int length,
unsigned char* check_sum) {
if (!check_sum)
return false;
*check_sum = 0;
if (!data)
return false;
// The inital and final constants are as used in the ATM HEC.
static const unsigned char kInitial = 0x00;
static const unsigned char kFinal = 0x55;
unsigned char crc = kInitial;
for (int i = 0; i < length; ++i) {
crc = kCrcTable[(data[i] ^ crc) & 0xFFU];
}
*check_sum = crc ^ kFinal;
return true;
}
bool Crc8::Verify(const unsigned char* data, int length,
unsigned char check_sum, bool* matches) {
if (!matches)
return false;
*matches = false;
if (!data)
return false;
unsigned char calculated_crc;
if (!Generate(data, length, &calculated_crc))
return false;
*matches = check_sum == calculated_crc;
return true;
}
} // namespace rlz_lib

24
rlz/lib/crc8.h Normal file

@ -0,0 +1,24 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Crc8 utility functions.
#ifndef RLZ_LIB_CRC8_H_
#define RLZ_LIB_CRC8_H_
namespace rlz_lib {
// CRC-8 methods:
class Crc8 {
public:
static bool Generate(const unsigned char* data,
int length,
unsigned char* check_sum);
static bool Verify(const unsigned char* data,
int length,
unsigned char checksum,
bool * matches);
};
}; // namespace rlz_lib
#endif // RLZ_LIB_CRC8_H_

51
rlz/lib/crc8_unittest.cc Normal file

@ -0,0 +1,51 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Uniitest for data encryption functions.
#include "base/logging.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "rlz/lib/crc8.h"
TEST(Crc8Unittest, TestCrc8) {
struct Data {
char string[10];
// Externally calculated checksums use
// http://www.zorc.breitbandkatze.de/crc.html
// with the ATM HEC paramters:
// CRC-8, Polynomial 0x07, Initial value 0x00, Final XOR value 0x55
// (direct, don't reverse data byes, don't reverse CRC before final XOR)
unsigned char external_crc;
int random_byte;
unsigned char corrupt_value;
} data[] = {
{"Google", 0x01, 2, 0x53},
{"GOOGLE", 0xA6, 4, 0x11},
{"My CRC 8!", 0xDC, 0, 0x50},
};
unsigned char* bytes;
unsigned char crc;
bool matches;
int length;
for (size_t i = 0; i < sizeof(data) / sizeof(data[0]); ++i) {
bytes = reinterpret_cast<unsigned char*>(data[i].string);
crc = 0;
matches = false;
length = strlen(data[i].string);
// Calculate CRC and compare against external value.
rlz_lib::Crc8::Generate(bytes, length, &crc);
EXPECT_TRUE(crc == data[i].external_crc);
rlz_lib::Crc8::Verify(bytes, length, crc, &matches);
EXPECT_TRUE(matches);
// Corrupt string and see if CRC still matches.
data[i].string[data[i].random_byte] = data[i].corrupt_value;
rlz_lib::Crc8::Verify(bytes, length, crc, &matches);
EXPECT_FALSE(matches);
}
}

354
rlz/lib/financial_ping.cc Normal file

@ -0,0 +1,354 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Library functions related to the Financial Server ping.
#include "rlz/lib/financial_ping.h"
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/utf_string_conversions.h"
#include "rlz/lib/assert.h"
#include "rlz/lib/lib_values.h"
#include "rlz/lib/machine_id.h"
#include "rlz/lib/rlz_lib.h"
#include "rlz/lib/rlz_value_store.h"
#include "rlz/lib/string_utils.h"
#if !defined(OS_WIN)
#include "base/time.h"
#endif
#if defined(RLZ_NETWORK_IMPLEMENTATION_WIN_INET)
#include <windows.h>
#include <wininet.h>
namespace {
class InternetHandle {
public:
InternetHandle(HINTERNET handle) { handle_ = handle; }
~InternetHandle() { if (handle_) InternetCloseHandle(handle_); }
operator HINTERNET() const { return handle_; }
bool operator!() const { return (handle_ == NULL); }
private:
HINTERNET handle_;
};
} // namespace
#else
#include "base/bind.h"
#include "base/message_loop.h"
#include "base/time.h"
#include "googleurl/src/gurl.h"
#include "net/base/load_flags.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_fetcher_delegate.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
#endif
namespace {
// Returns the time relative to a fixed point in the past in multiples of
// 100 ns stepts. The point in the past is arbitrary but can't change, as the
// result of this value is stored on disk.
int64 GetSystemTimeAsInt64() {
#if defined(OS_WIN)
FILETIME now_as_file_time;
// Relative to Jan 1, 1601 (UTC).
GetSystemTimeAsFileTime(&now_as_file_time);
LARGE_INTEGER integer;
integer.HighPart = now_as_file_time.dwHighDateTime;
integer.LowPart = now_as_file_time.dwLowDateTime;
return integer.QuadPart;
#else
// Seconds since epoch (Jan 1, 1970).
double now_seconds = base::Time::Now().ToDoubleT();
return static_cast<int64>(now_seconds * 1000 * 1000 * 10);
#endif
}
} // namespace
namespace rlz_lib {
bool FinancialPing::FormRequest(Product product,
const AccessPoint* access_points, const char* product_signature,
const char* product_brand, const char* product_id,
const char* product_lang, bool exclude_machine_id,
std::string* request) {
if (!request) {
ASSERT_STRING("FinancialPing::FormRequest: request is NULL");
return false;
}
request->clear();
ScopedRlzValueStoreLock lock;
RlzValueStore* store = lock.GetStore();
if (!store || !store->HasAccess(RlzValueStore::kReadAccess))
return false;
if (!access_points) {
ASSERT_STRING("FinancialPing::FormRequest: access_points is NULL");
return false;
}
if (!product_signature) {
ASSERT_STRING("FinancialPing::FormRequest: product_signature is NULL");
return false;
}
if (!SupplementaryBranding::GetBrand().empty()) {
if (SupplementaryBranding::GetBrand() != product_brand) {
ASSERT_STRING("FinancialPing::FormRequest: supplementary branding bad");
return false;
}
}
base::StringAppendF(request, "%s?", kFinancialPingPath);
// Add the signature, brand, product id and language.
base::StringAppendF(request, "%s=%s", kProductSignatureCgiVariable,
product_signature);
if (product_brand)
base::StringAppendF(request, "&%s=%s", kProductBrandCgiVariable,
product_brand);
if (product_id)
base::StringAppendF(request, "&%s=%s", kProductIdCgiVariable, product_id);
if (product_lang)
base::StringAppendF(request, "&%s=%s", kProductLanguageCgiVariable,
product_lang);
// Add the product events.
char cgi[kMaxCgiLength + 1];
cgi[0] = 0;
bool has_events = GetProductEventsAsCgi(product, cgi, arraysize(cgi));
if (has_events)
base::StringAppendF(request, "&%s", cgi);
// If we don't have any events, we should ping all the AP's on the system
// that we know about and have a current RLZ value, even if they are not
// used by this product.
AccessPoint all_points[LAST_ACCESS_POINT];
if (!has_events) {
char rlz[kMaxRlzLength + 1];
int idx = 0;
for (int ap = NO_ACCESS_POINT + 1; ap < LAST_ACCESS_POINT; ap++) {
rlz[0] = 0;
AccessPoint point = static_cast<AccessPoint>(ap);
if (GetAccessPointRlz(point, rlz, arraysize(rlz)) &&
rlz[0] != '\0')
all_points[idx++] = point;
}
all_points[idx] = NO_ACCESS_POINT;
}
// Add the RLZ's and the DCC if needed. This is the same as get PingParams.
// This will also include the RLZ Exchange Protocol CGI Argument.
cgi[0] = 0;
if (GetPingParams(product, has_events ? access_points : all_points,
cgi, arraysize(cgi)))
base::StringAppendF(request, "&%s", cgi);
if (has_events && !exclude_machine_id) {
std::string machine_id;
if (GetMachineId(&machine_id)) {
base::StringAppendF(request, "&%s=%s", kMachineIdCgiVariable,
machine_id.c_str());
}
}
return true;
}
#if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
// The URLRequestContextGetter used by FinancialPing::PingServer().
net::URLRequestContextGetter* g_context;
bool FinancialPing::SetURLRequestContext(
net::URLRequestContextGetter* context) {
ScopedRlzValueStoreLock lock;
RlzValueStore* store = lock.GetStore();
if (!store)
return false;
g_context = context;
return true;
}
namespace {
class FinancialPingUrlFetcherDelegate : public net::URLFetcherDelegate {
public:
FinancialPingUrlFetcherDelegate(MessageLoop* loop) : loop_(loop) { }
virtual void OnURLFetchComplete(const net::URLFetcher* source);
private:
MessageLoop* loop_;
};
void FinancialPingUrlFetcherDelegate::OnURLFetchComplete(
const net::URLFetcher* source) {
loop_->Quit();
}
} // namespace
#endif
bool FinancialPing::PingServer(const char* request, std::string* response) {
if (!response)
return false;
response->clear();
#if defined(RLZ_NETWORK_IMPLEMENTATION_WIN_INET)
// Initialize WinInet.
InternetHandle inet_handle = InternetOpenA(kFinancialPingUserAgent,
INTERNET_OPEN_TYPE_PRECONFIG,
NULL, NULL, 0);
if (!inet_handle)
return false;
// Open network connection.
InternetHandle connection_handle = InternetConnectA(inet_handle,
kFinancialServer, kFinancialPort, "", "", INTERNET_SERVICE_HTTP,
INTERNET_FLAG_NO_CACHE_WRITE, 0);
if (!connection_handle)
return false;
// Prepare the HTTP request.
InternetHandle http_handle = HttpOpenRequestA(connection_handle,
"GET", request, NULL, NULL, kFinancialPingResponseObjects,
INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES, NULL);
if (!http_handle)
return false;
// Timeouts are probably:
// INTERNET_OPTION_SEND_TIMEOUT, INTERNET_OPTION_RECEIVE_TIMEOUT
// Send the HTTP request. Note: Fails if user is working in off-line mode.
if (!HttpSendRequest(http_handle, NULL, 0, NULL, 0))
return false;
// Check the response status.
DWORD status;
DWORD status_size = sizeof(status);
if (!HttpQueryInfo(http_handle, HTTP_QUERY_STATUS_CODE |
HTTP_QUERY_FLAG_NUMBER, &status, &status_size, NULL) ||
200 != status)
return false;
// Get the response text.
scoped_array<char> buffer(new char[kMaxPingResponseLength]);
if (buffer.get() == NULL)
return false;
DWORD bytes_read = 0;
while (InternetReadFile(http_handle, buffer.get(), kMaxPingResponseLength,
&bytes_read) && bytes_read > 0) {
response->append(buffer.get(), bytes_read);
bytes_read = 0;
};
return true;
#else
// Run a blocking event loop to match the win inet implementation.
MessageLoop loop;
FinancialPingUrlFetcherDelegate delegate(&loop);
std::string url = base::StringPrintf("http://%s:%d%s",
kFinancialServer, kFinancialPort,
request);
scoped_ptr<net::URLFetcher> fetcher(net::URLFetcher::Create(
GURL(url), net::URLFetcher::GET, &delegate));
fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE |
net::LOAD_DO_NOT_SEND_AUTH_DATA |
net::LOAD_DO_NOT_PROMPT_FOR_LOGIN |
net::LOAD_DO_NOT_SEND_COOKIES |
net::LOAD_DO_NOT_SAVE_COOKIES);
// Ensure rlz_lib::SetURLRequestContext() has been called before sending
// pings.
CHECK(g_context);
fetcher->SetRequestContext(g_context);
const base::TimeDelta kTimeout = base::TimeDelta::FromMinutes(5);
loop.PostTask(
FROM_HERE,
base::Bind(&net::URLFetcher::Start, base::Unretained(fetcher.get())));
loop.PostNonNestableDelayedTask(
FROM_HERE, MessageLoop::QuitClosure(), kTimeout);
loop.Run();
if (fetcher->GetResponseCode() != 200)
return false;
return fetcher->GetResponseAsString(response);
#endif
}
bool FinancialPing::IsPingTime(Product product, bool no_delay) {
ScopedRlzValueStoreLock lock;
RlzValueStore* store = lock.GetStore();
if (!store || !store->HasAccess(RlzValueStore::kReadAccess))
return false;
int64 last_ping = 0;
if (!store->ReadPingTime(product, &last_ping))
return true;
uint64 now = GetSystemTimeAsInt64();
int64 interval = now - last_ping;
// If interval is negative, clock was probably reset. So ping.
if (interval < 0)
return true;
// Check if this product has any unreported events.
char cgi[kMaxCgiLength + 1];
cgi[0] = 0;
bool has_events = GetProductEventsAsCgi(product, cgi, arraysize(cgi));
if (no_delay && has_events)
return true;
return interval >= (has_events ? kEventsPingInterval : kNoEventsPingInterval);
}
bool FinancialPing::UpdateLastPingTime(Product product) {
ScopedRlzValueStoreLock lock;
RlzValueStore* store = lock.GetStore();
if (!store || !store->HasAccess(RlzValueStore::kWriteAccess))
return false;
uint64 now = GetSystemTimeAsInt64();
return store->WritePingTime(product, now);
}
bool FinancialPing::ClearLastPingTime(Product product) {
ScopedRlzValueStoreLock lock;
RlzValueStore* store = lock.GetStore();
if (!store || !store->HasAccess(RlzValueStore::kWriteAccess))
return false;
return store->ClearPingTime(product);
}
} // namespace

64
rlz/lib/financial_ping.h Normal file

@ -0,0 +1,64 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Library functions related to the Financial Server ping.
#ifndef RLZ_LIB_FINANCIAL_PING_H_
#define RLZ_LIB_FINANCIAL_PING_H_
#include <string>
#include "rlz/lib/rlz_enums.h"
#if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
namespace net {
class URLRequestContextGetter;
} // namespace net
#endif
namespace rlz_lib {
class FinancialPing {
public:
// Form the HTTP request to send to the PSO server.
// Will look something like:
// /pso/ping?as=swg&brand=GGLD&id=124&hl=en&
// events=I7S&rep=1&rlz=I7:val,W1:&dcc=dval
static bool FormRequest(Product product, const AccessPoint* access_points,
const char* product_signature,
const char* product_brand, const char* product_id,
const char* product_lang, bool exclude_machine_id,
std::string* request);
// Returns whether the time is right to send a ping.
// If no_delay is true, this should always ping if there are events,
// or one week has passed since last_ping when there are no new events.
// If no_delay is false, this should ping if current time < last_ping time
// (case of time reset) or if one day has passed since last_ping and there
// are events, or one week has passed since last_ping when there are
// no new events.
static bool IsPingTime(Product product, bool no_delay);
// Set the last ping time to be now. Writes to RlzValueStore.
static bool UpdateLastPingTime(Product product);
// Clear the last ping time - should be called on uninstall.
// Writes to RlzValueStore.
static bool ClearLastPingTime(Product product);
// Ping the financial server with request. Writes to RlzValueStore.
static bool PingServer(const char* request, std::string* response);
#if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
static bool SetURLRequestContext(net::URLRequestContextGetter* context);
#endif
private:
FinancialPing() {}
~FinancialPing() {}
};
} // namespace rlz_lib
#endif // RLZ_LIB_FINANCIAL_PING_H_

@ -0,0 +1,293 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// A test application for the FinancialPing class.
//
// These tests should not be executed on the build server:
// - They modify machine state (registry).
//
// These tests require write access to HKCU and HKLM.
//
// The "GGLA" brand is used to test the normal code flow of the code, and the
// "TEST" brand is used to test the supplementary brand code code flow. In one
// case below, the brand "GOOG" is used because the code wants to use a brand
// that is neither of the two mentioned above.
#include "rlz/lib/financial_ping.h"
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/utf_string_conversions.h"
#include "rlz/lib/lib_values.h"
#include "rlz/lib/machine_id.h"
#include "rlz/lib/rlz_lib.h"
#include "rlz/lib/rlz_value_store.h"
#include "rlz/test/rlz_test_helpers.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_WIN)
#include "rlz/win/lib/machine_deal.h"
#else
#include "base/time.h"
#endif
namespace {
// Must match the implementation in file_time.cc.
int64 GetSystemTimeAsInt64() {
#if defined(OS_WIN)
FILETIME now_as_file_time;
GetSystemTimeAsFileTime(&now_as_file_time);
LARGE_INTEGER integer;
integer.HighPart = now_as_file_time.dwHighDateTime;
integer.LowPart = now_as_file_time.dwLowDateTime;
return integer.QuadPart;
#else
double now_seconds = base::Time::Now().ToDoubleT();
return static_cast<int64>(now_seconds * 1000 * 1000 * 10);
#endif
}
// Ping times in 100-nanosecond intervals.
const int64 k1MinuteInterval = 60LL * 10000000LL; // 1 minute
} // namespace anonymous
class FinancialPingTest : public RlzLibTestBase {
};
TEST_F(FinancialPingTest, FormRequest) {
std::string brand_string = rlz_lib::SupplementaryBranding::GetBrand();
const char* brand = brand_string.empty() ? "GGLA" : brand_string.c_str();
#if defined(OS_WIN)
EXPECT_TRUE(rlz_lib::MachineDealCode::Set("dcc_value"));
#define DCC_PARAM "&dcc=dcc_value"
#else
#define DCC_PARAM ""
#endif
EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX,
"TbRlzValue"));
EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
rlz_lib::AccessPoint points[] =
{rlz_lib::IETB_SEARCH_BOX, rlz_lib::NO_ACCESS_POINT,
rlz_lib::NO_ACCESS_POINT};
std::string machine_id;
bool got_machine_id = rlz_lib::GetMachineId(&machine_id);
std::string request;
EXPECT_TRUE(rlz_lib::FinancialPing::FormRequest(rlz_lib::TOOLBAR_NOTIFIER,
points, "swg", brand, NULL, "en", false, &request));
std::string expected_response;
base::StringAppendF(&expected_response,
"/tools/pso/ping?as=swg&brand=%s&hl=en&"
"events=I7S,W1I&rep=2&rlz=T4:TbRlzValue" DCC_PARAM
, brand);
if (got_machine_id)
base::StringAppendF(&expected_response, "&id=%s", machine_id.c_str());
EXPECT_EQ(expected_response, request);
EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, ""));
EXPECT_TRUE(rlz_lib::FinancialPing::FormRequest(rlz_lib::TOOLBAR_NOTIFIER,
points, "swg", brand, "IdOk2", NULL, false, &request));
expected_response.clear();
base::StringAppendF(&expected_response,
"/tools/pso/ping?as=swg&brand=%s&pid=IdOk2&"
"events=I7S,W1I&rep=2&rlz=T4:" DCC_PARAM, brand);
if (got_machine_id)
base::StringAppendF(&expected_response, "&id=%s", machine_id.c_str());
EXPECT_EQ(expected_response, request);
EXPECT_TRUE(rlz_lib::FinancialPing::FormRequest(rlz_lib::TOOLBAR_NOTIFIER,
points, "swg", brand, "IdOk", NULL, true, &request));
expected_response.clear();
base::StringAppendF(&expected_response,
"/tools/pso/ping?as=swg&brand=%s&pid=IdOk&"
"events=I7S,W1I&rep=2&rlz=T4:" DCC_PARAM, brand);
EXPECT_EQ(expected_response, request);
EXPECT_TRUE(rlz_lib::FinancialPing::FormRequest(rlz_lib::TOOLBAR_NOTIFIER,
points, "swg", brand, NULL, NULL, true, &request));
expected_response.clear();
base::StringAppendF(&expected_response,
"/tools/pso/ping?as=swg&brand=%s&events=I7S,W1I&rep=2"
"&rlz=T4:" DCC_PARAM, brand);
EXPECT_EQ(expected_response, request);
// Clear all events.
EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
// Clear all RLZs.
char rlz[rlz_lib::kMaxRlzLength + 1];
for (int ap = rlz_lib::NO_ACCESS_POINT + 1;
ap < rlz_lib::LAST_ACCESS_POINT; ap++) {
rlz[0] = 0;
rlz_lib::AccessPoint point = static_cast<rlz_lib::AccessPoint>(ap);
if (rlz_lib::GetAccessPointRlz(point, rlz, arraysize(rlz)) && rlz[0]) {
rlz_lib::SetAccessPointRlz(point, "");
}
}
EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX,
"TbRlzValue"));
EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::QUICK_SEARCH_BOX,
"QsbRlzValue"));
EXPECT_TRUE(rlz_lib::FinancialPing::FormRequest(rlz_lib::TOOLBAR_NOTIFIER,
points, "swg", brand, NULL, NULL, false, &request));
expected_response.clear();
base::StringAppendF(&expected_response,
"/tools/pso/ping?as=swg&brand=%s&rep=2&rlz=T4:TbRlzValue,"
"Q1:QsbRlzValue" DCC_PARAM, brand);
EXPECT_STREQ(expected_response.c_str(), request.c_str());
if (!GetAccessPointRlz(rlz_lib::IE_HOME_PAGE, rlz, arraysize(rlz))) {
points[2] = rlz_lib::IE_HOME_PAGE;
EXPECT_TRUE(rlz_lib::FinancialPing::FormRequest(rlz_lib::TOOLBAR_NOTIFIER,
points, "swg", brand, "MyId", "en-US", true, &request));
expected_response.clear();
base::StringAppendF(&expected_response,
"/tools/pso/ping?as=swg&brand=%s&hl=en-US&pid=MyId&rep=2"
"&rlz=T4:TbRlzValue,Q1:QsbRlzValue" DCC_PARAM, brand);
EXPECT_STREQ(expected_response.c_str(), request.c_str());
}
}
TEST_F(FinancialPingTest, FormRequestBadBrand) {
rlz_lib::AccessPoint points[] =
{rlz_lib::IETB_SEARCH_BOX, rlz_lib::NO_ACCESS_POINT,
rlz_lib::NO_ACCESS_POINT};
std::string request;
bool ok = rlz_lib::FinancialPing::FormRequest(rlz_lib::TOOLBAR_NOTIFIER,
points, "swg", "GOOG", NULL, "en", false, &request);
EXPECT_EQ(rlz_lib::SupplementaryBranding::GetBrand().empty(), ok);
}
static void SetLastPingTime(int64 time, rlz_lib::Product product) {
rlz_lib::ScopedRlzValueStoreLock lock;
rlz_lib::RlzValueStore* store = lock.GetStore();
ASSERT_TRUE(store);
ASSERT_TRUE(store->HasAccess(rlz_lib::RlzValueStore::kWriteAccess));
store->WritePingTime(product, time);
}
TEST_F(FinancialPingTest, IsPingTime) {
int64 now = GetSystemTimeAsInt64();
int64 last_ping = now - rlz_lib::kEventsPingInterval - k1MinuteInterval;
SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER);
// No events, last ping just over a day ago.
EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
EXPECT_FALSE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
false));
// Has events, last ping just over a day ago.
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
false));
// Has events, last ping just under a day ago.
last_ping = now - rlz_lib::kEventsPingInterval + k1MinuteInterval;
SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER);
EXPECT_FALSE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
false));
EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
// No events, last ping just under a week ago.
last_ping = now - rlz_lib::kNoEventsPingInterval + k1MinuteInterval;
SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER);
EXPECT_FALSE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
false));
// No events, last ping just over a week ago.
last_ping = now - rlz_lib::kNoEventsPingInterval - k1MinuteInterval;
SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER);
EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
false));
// Last ping was in future (invalid).
last_ping = now + k1MinuteInterval;
SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER);
EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
false));
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
false));
}
TEST_F(FinancialPingTest, BrandingIsPingTime) {
// Don't run these tests if a supplementary brand is already in place. That
// way we can control the branding.
if (!rlz_lib::SupplementaryBranding::GetBrand().empty())
return;
int64 now = GetSystemTimeAsInt64();
int64 last_ping = now - rlz_lib::kEventsPingInterval - k1MinuteInterval;
SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER);
// Has events, last ping just over a day ago.
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
false));
{
rlz_lib::SupplementaryBranding branding("TEST");
SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER);
// Has events, last ping just over a day ago.
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
false));
}
last_ping = now - k1MinuteInterval;
SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER);
EXPECT_FALSE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
false));
{
rlz_lib::SupplementaryBranding branding("TEST");
EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
false));
}
}
TEST_F(FinancialPingTest, ClearLastPingTime) {
int64 now = GetSystemTimeAsInt64();
int64 last_ping = now - rlz_lib::kEventsPingInterval + k1MinuteInterval;
SetLastPingTime(last_ping, rlz_lib::TOOLBAR_NOTIFIER);
// Has events, last ping just under a day ago.
EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
EXPECT_FALSE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
false));
EXPECT_TRUE(rlz_lib::FinancialPing::ClearLastPingTime(
rlz_lib::TOOLBAR_NOTIFIER));
EXPECT_TRUE(rlz_lib::FinancialPing::IsPingTime(rlz_lib::TOOLBAR_NOTIFIER,
false));
}

208
rlz/lib/lib_values.cc Normal file

@ -0,0 +1,208 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Key and value names of the location of the RLZ shared state.
#include "rlz/lib/lib_values.h"
#include "base/stringprintf.h"
#include "rlz/lib/assert.h"
namespace rlz_lib {
//
// Ping information.
//
// rep=2: includes the new stateful events.
const char kProtocolCgiArgument[] = "rep=2";
const char kEventsCgiVariable[] = "events";
const char kStatefulEventsCgiVariable[] = "stateful-events";
const char kEventsCgiSeparator = ',';
const char kRlzCgiVariable[] = "rlz";
const char kRlzCgiSeparator[] = ",";
const char kRlzCgiIndicator[] = ":";
const char kProductSignatureCgiVariable[] = "as";
const char kProductBrandCgiVariable[] = "brand";
const char kProductLanguageCgiVariable[] = "hl";
const char kProductIdCgiVariable[] = "pid";
const char kDccCgiVariable[] = "dcc";
const char kRlsCgiVariable[] = "rls";
const char kMachineIdCgiVariable[] = "id";
const char kSetDccResponseVariable[] = "set_dcc";
//
// Financial server information.
//
const char kFinancialPingPath[] = "/tools/pso/ping";
const char kFinancialServer[] = "clients1.google.com";
const int kFinancialPort = 80;
// Ping times in 100-nanosecond intervals.
const int64 kEventsPingInterval = 24LL * 3600LL * 10000000LL; // 1 day
const int64 kNoEventsPingInterval = kEventsPingInterval * 7LL; // 1 week
const char kFinancialPingUserAgent[] = "Mozilla/4.0 (compatible; Win32)";
const char* kFinancialPingResponseObjects[] = { "text/*", NULL };
//
// AccessPoint and Event names.
//
//
const char* GetAccessPointName(AccessPoint point) {
switch (point) {
case NO_ACCESS_POINT: return "";
case IE_DEFAULT_SEARCH: return "I7";
case IE_HOME_PAGE: return "W1";
case IETB_SEARCH_BOX: return "T4";
case QUICK_SEARCH_BOX: return "Q1";
case GD_DESKBAND: return "D1";
case GD_SEARCH_GADGET: return "D2";
case GD_WEB_SERVER: return "D3";
case GD_OUTLOOK: return "D4";
case CHROME_OMNIBOX: return "C1";
case CHROME_HOME_PAGE: return "C2";
case FFTB2_BOX: return "B2";
case FFTB3_BOX: return "B3";
case PINYIN_IME_BHO: return "N1";
case IGOOGLE_WEBPAGE: return "G1";
case MOBILE_IDLE_SCREEN_BLACKBERRY: return "H1";
case MOBILE_IDLE_SCREEN_WINMOB: return "H2";
case MOBILE_IDLE_SCREEN_SYMBIAN: return "H3";
case FF_HOME_PAGE: return "R0";
case FF_SEARCH_BOX: return "R1";
case IE_BROWSED_PAGE: return "R2";
case QSB_WIN_BOX: return "R3";
case WEBAPPS_CALENDAR: return "R4";
case WEBAPPS_DOCS: return "R5";
case WEBAPPS_GMAIL: return "R6";
case IETB_LINKDOCTOR: return "R7";
case FFTB_LINKDOCTOR: return "R8";
case IETB7_SEARCH_BOX: return "T7";
case TB8_SEARCH_BOX: return "T8";
case CHROME_FRAME: return "C3";
case PARTNER_AP_1: return "V1";
case PARTNER_AP_2: return "V2";
case PARTNER_AP_3: return "V3";
case PARTNER_AP_4: return "V4";
case PARTNER_AP_5: return "V5";
case UNDEFINED_AP_H: return "RH";
case UNDEFINED_AP_I: return "RI";
case UNDEFINED_AP_J: return "RJ";
case UNDEFINED_AP_K: return "RK";
case UNDEFINED_AP_L: return "RL";
case UNDEFINED_AP_M: return "RM";
case UNDEFINED_AP_N: return "RN";
case UNDEFINED_AP_O: return "RO";
case UNDEFINED_AP_P: return "RP";
case UNDEFINED_AP_Q: return "RQ";
case UNDEFINED_AP_R: return "RR";
case UNDEFINED_AP_S: return "RS";
case UNDEFINED_AP_T: return "RT";
case UNDEFINED_AP_U: return "RU";
case UNDEFINED_AP_V: return "RV";
case UNDEFINED_AP_W: return "RW";
case UNDEFINED_AP_X: return "RX";
case UNDEFINED_AP_Y: return "RY";
case UNDEFINED_AP_Z: return "RZ";
case PACK_AP0: return "U0";
case PACK_AP1: return "U1";
case PACK_AP2: return "U2";
case PACK_AP3: return "U3";
case PACK_AP4: return "U4";
case PACK_AP5: return "U5";
case PACK_AP6: return "U6";
case PACK_AP7: return "U7";
case PACK_AP8: return "U8";
case PACK_AP9: return "U9";
case PACK_AP10: return "UA";
case PACK_AP11: return "UB";
case PACK_AP12: return "UC";
case PACK_AP13: return "UD";
case LAST_ACCESS_POINT: ; // Fall through.
}
ASSERT_STRING("GetAccessPointName: Unknown Access Point");
return NULL;
}
bool GetAccessPointFromName(const char* name, AccessPoint* point) {
if (!point) {
ASSERT_STRING("GetAccessPointFromName: point is NULL");
return false;
}
*point = NO_ACCESS_POINT;
if (!name)
return false;
for (int i = NO_ACCESS_POINT; i < LAST_ACCESS_POINT; i++)
if (strcmp(name, GetAccessPointName(static_cast<AccessPoint>(i))) == 0) {
*point = static_cast<AccessPoint>(i);
return true;
}
return false;
}
const char* GetEventName(Event event) {
switch (event) {
case INVALID_EVENT: return "";
case INSTALL: return "I";
case SET_TO_GOOGLE: return "S";
case FIRST_SEARCH: return "F";
case REPORT_RLS: return "R";
case ACTIVATE: return "A";
case LAST_EVENT: ; // Fall through.
}
ASSERT_STRING("GetPointName: Unknown Event");
return NULL;
}
bool GetEventFromName(const char* name, Event* event) {
if (!event) {
ASSERT_STRING("GetEventFromName: event is NULL");
return false;
}
*event = INVALID_EVENT;
if (!name)
return false;
for (int i = INVALID_EVENT; i < LAST_EVENT; i++)
if (strcmp(name, GetEventName(static_cast<Event>(i))) == 0) {
*event = static_cast<Event>(i);
return true;
}
return false;
}
const char* GetProductName(Product product) {
switch (product) {
case IE_TOOLBAR: return "T";
case TOOLBAR_NOTIFIER: return "P";
case PACK: return "U";
case DESKTOP: return "D";
case CHROME: return "C";
case FF_TOOLBAR: return "B";
case QSB_WIN: return "K";
case WEBAPPS: return "W";
case PINYIN_IME: return "N";
case PARTNER: return "V";
}
ASSERT_STRING("GetProductName: Unknown Product");
return "";
}
} // namespace rlz_lib

99
rlz/lib/lib_values.h Normal file

@ -0,0 +1,99 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Key and value names of the location of the RLZ shared state.
#ifndef RLZ_LIB_LIB_VALUES_H_
#define RLZ_LIB_LIB_VALUES_H_
#include "base/basictypes.h"
#include "rlz/lib/rlz_enums.h"
namespace rlz_lib {
//
// Ping CGI arguments:
//
// Events are reported as (without spaces):
// kEventsCgiVariable = <AccessPoint1><Event1> kEventsCgiSeparator <P2><E2>...
//
// Event responses from the server look like:
// kEventsCgiVariable : <AccessPoint1><Event1> kEventsCgiSeparator <P2><E2>...
//
// RLZ's are reported as (without spaces):
// kRlzCgiVariable = <AccessPoint> <kRlzCgiIndicator> <RLZ value>
// <kRlzCgiSeparator> <AP2><Indicator><V2><Separator> ....
//
// RLZ responses from the server look like (without spaces):
// kRlzCgiVariable<Access Point> : <RLZ value>
//
// DCC if reported should look like (without spaces):
// kDccCgiVariable = <DCC Value>
//
// RLS if reported should look like (without spaces):
// kRlsCgiVariable = <RLS Value>
//
// Machine ID if reported should look like (without spaces):
// kMachineIdCgiVariable = <Machine ID Value>
//
// A server response setting / confirming the DCC will look like (no spaces):
// kDccCgiVariable : <DCC Value>
//
// Each ping to the server must also contain kProtocolCgiArgument as well.
//
// Pings may also contain (but not necessarily controlled by this Lib):
// - The product signature: kProductSignatureCgiVariable = <signature>
// - The product brand: kProductBrandCgiVariable = <brand>
// - The product installation ID: kProductIdCgiVariable = <id>
extern const char kEventsCgiVariable[];
extern const char kStatefulEventsCgiVariable[];
extern const char kEventsCgiSeparator;
extern const char kDccCgiVariable[];
extern const char kProtocolCgiArgument[];
extern const char kProductSignatureCgiVariable[];
extern const char kProductBrandCgiVariable[];
extern const char kProductLanguageCgiVariable[];
extern const char kProductIdCgiVariable[];
extern const char kRlzCgiVariable[];
extern const char kRlzCgiSeparator[];
extern const char kRlzCgiIndicator[];
extern const char kRlsCgiVariable[];
extern const char kMachineIdCgiVariable[];
extern const char kSetDccResponseVariable[];
//
// Financial ping server information.
//
extern const char kFinancialPingPath[];
extern const char kFinancialServer[];
extern const int kFinancialPort;
extern const int64 kEventsPingInterval;
extern const int64 kNoEventsPingInterval;
extern const char kFinancialPingUserAgent[];
extern const char* kFinancialPingResponseObjects[];
//
// The names for AccessPoints and Events that we use MUST be the same
// as those used/understood by the server.
//
const char* GetAccessPointName(AccessPoint point);
bool GetAccessPointFromName(const char* name, AccessPoint* point);
const char* GetEventName(Event event);
bool GetEventFromName(const char* name, Event* event);
// The names for products are used only client-side.
const char* GetProductName(Product product);
} // namespace rlz_lib
#endif // RLZ_LIB_LIB_VALUES_H_

@ -0,0 +1,61 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "rlz/lib/lib_values.h"
#include "base/logging.h"
#include "rlz/lib/assert.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
TEST(LibValuesUnittest, GetAccessPointFromName) {
rlz_lib::SetExpectedAssertion("GetAccessPointFromName: point is NULL");
EXPECT_FALSE(rlz_lib::GetAccessPointFromName("", NULL));
rlz_lib::SetExpectedAssertion("");
rlz_lib::AccessPoint point;
EXPECT_FALSE(rlz_lib::GetAccessPointFromName(NULL, &point));
EXPECT_EQ(rlz_lib::NO_ACCESS_POINT, point);
EXPECT_TRUE(rlz_lib::GetAccessPointFromName("", &point));
EXPECT_EQ(rlz_lib::NO_ACCESS_POINT, point);
EXPECT_FALSE(rlz_lib::GetAccessPointFromName("i1", &point));
EXPECT_EQ(rlz_lib::NO_ACCESS_POINT, point);
EXPECT_TRUE(rlz_lib::GetAccessPointFromName("I7", &point));
EXPECT_EQ(rlz_lib::IE_DEFAULT_SEARCH, point);
EXPECT_TRUE(rlz_lib::GetAccessPointFromName("T4", &point));
EXPECT_EQ(rlz_lib::IETB_SEARCH_BOX, point);
EXPECT_FALSE(rlz_lib::GetAccessPointFromName("T4 ", &point));
EXPECT_EQ(rlz_lib::NO_ACCESS_POINT, point);
}
TEST(LibValuesUnittest, GetEventFromName) {
rlz_lib::SetExpectedAssertion("GetEventFromName: event is NULL");
EXPECT_FALSE(rlz_lib::GetEventFromName("", NULL));
rlz_lib::SetExpectedAssertion("");
rlz_lib::Event event;
EXPECT_FALSE(rlz_lib::GetEventFromName(NULL, &event));
EXPECT_EQ(rlz_lib::INVALID_EVENT, event);
EXPECT_TRUE(rlz_lib::GetEventFromName("", &event));
EXPECT_EQ(rlz_lib::INVALID_EVENT, event);
EXPECT_FALSE(rlz_lib::GetEventFromName("i1", &event));
EXPECT_EQ(rlz_lib::INVALID_EVENT, event);
EXPECT_TRUE(rlz_lib::GetEventFromName("I", &event));
EXPECT_EQ(rlz_lib::INSTALL, event);
EXPECT_TRUE(rlz_lib::GetEventFromName("F", &event));
EXPECT_EQ(rlz_lib::FIRST_SEARCH, event);
EXPECT_FALSE(rlz_lib::GetEventFromName("F ", &event));
EXPECT_EQ(rlz_lib::INVALID_EVENT, event);
}

84
rlz/lib/machine_id.cc Normal file

@ -0,0 +1,84 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "rlz/lib/machine_id.h"
#include "base/sha1.h"
#include "rlz/lib/assert.h"
#include "rlz/lib/crc8.h"
#include "rlz/lib/string_utils.h"
namespace rlz_lib {
bool GetMachineId(std::string* machine_id) {
if (!machine_id)
return false;
static std::string calculated_id;
static bool calculated = false;
if (calculated) {
*machine_id = calculated_id;
return true;
}
string16 sid_string;
int volume_id;
if (!GetRawMachineId(&sid_string, &volume_id))
return false;
if (!testing::GetMachineIdImpl(sid_string, volume_id, machine_id))
return false;
calculated = true;
calculated_id = *machine_id;
return true;
}
namespace testing {
bool GetMachineIdImpl(const string16& sid_string,
int volume_id,
std::string* machine_id) {
machine_id->clear();
// The ID should be the SID hash + the Hard Drive SNo. + checksum byte.
static const int kSizeWithoutChecksum = base::kSHA1Length + sizeof(int);
std::basic_string<unsigned char> id_binary(kSizeWithoutChecksum + 1, 0);
if (!sid_string.empty()) {
// In order to be compatible with the old version of RLZ, the hash of the
// SID must be done with all the original bytes from the unicode string.
// However, the chromebase SHA1 hash function takes only an std::string as
// input, so the unicode string needs to be converted to std::string
// "as is".
size_t byte_count = sid_string.size() * sizeof(string16::value_type);
const char* buffer = reinterpret_cast<const char*>(sid_string.c_str());
std::string sid_string_buffer(buffer, byte_count);
// Note that digest can have embedded nulls.
std::string digest(base::SHA1HashString(sid_string_buffer));
VERIFY(digest.size() == base::kSHA1Length);
std::copy(digest.begin(), digest.end(), id_binary.begin());
}
// Convert from int to binary (makes big-endian).
for (size_t i = 0; i < sizeof(int); i++) {
int shift_bits = 8 * (sizeof(int) - i - 1);
id_binary[base::kSHA1Length + i] = static_cast<unsigned char>(
(volume_id >> shift_bits) & 0xFF);
}
// Append the checksum byte.
if (!sid_string.empty() || (0 != volume_id))
rlz_lib::Crc8::Generate(id_binary.c_str(),
kSizeWithoutChecksum,
&id_binary[kSizeWithoutChecksum]);
return rlz_lib::BytesToString(
id_binary.c_str(), kSizeWithoutChecksum + 1, machine_id);
}
} // namespace testing
} // namespace rlz_lib

34
rlz/lib/machine_id.h Normal file

@ -0,0 +1,34 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef RLZ_LIB_MACHINE_ID_H_
#define RLZ_LIB_MACHINE_ID_H_
#include "base/string16.h"
#include <string>
namespace rlz_lib {
// Gets the unique ID for the machine used for RLZ tracking purposes. On
// Windows, this ID is derived from the Windows machine SID, and is the string
// representation of a 20 byte hash + 4 bytes volum id + a 1 byte checksum.
// Included in financial pings with events, unless explicitly forbidden by the
// calling application.
bool GetMachineId(std::string* machine_id);
// Retrieves a raw machine identifier string and a machine-specific
// 4 byte value. GetMachineId() will SHA1 |data|, append |more_data|, compute
// the Crc8 of that, and return a hex-encoded string of that data.
bool GetRawMachineId(string16* data, int* more_data);
namespace testing {
bool GetMachineIdImpl(const string16& sid_string,
int volume_id,
std::string* machine_id);
} // namespace testing
} // namespace rlz_lib
#endif // RLZ_LIB_MACHINE_ID_H_

@ -0,0 +1,20 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "rlz/lib/machine_id.h"
#include "base/string16.h"
#include "base/utf_string_conversions.h"
#include "rlz/test/rlz_test_helpers.h"
#include "testing/gtest/include/gtest/gtest.h"
// This test will fail if the behavior of GetMachineId changes.
TEST(MachineDealCodeTestMachineId, MachineId) {
string16 computer_sid(UTF8ToUTF16(
"S-1-5-21-2345599882-2448789067-1921365677"));
std::string id;
rlz_lib::testing::GetMachineIdImpl(computer_sid, 2651229008, &id);
EXPECT_STREQ("A341BA986A7E86840688977FCF20C86E253F00919E068B50F8",
id.c_str());
}

132
rlz/lib/rlz_enums.h Normal file

@ -0,0 +1,132 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef RLZ_LIB_RLZ_ENUMS_H_
#define RLZ_LIB_RLZ_ENUMS_H_
namespace rlz_lib {
// An Access Point offers a way to search using Google.
enum AccessPoint {
NO_ACCESS_POINT = 0,
// Access points on Windows PCs.
IE_DEFAULT_SEARCH, // The IE7+ chrome search box next to the address bar.
IE_HOME_PAGE, // Search box on IE 5+ primary home page when Google.
IETB_SEARCH_BOX, // IE Toolbar v4+ search box.
QUICK_SEARCH_BOX, // Search box brought up by ctrl-ctrl key sequence,
// distributed as a part of Google Desktop
GD_DESKBAND, // Search box in deskbar when GD in deskbar mode.
GD_SEARCH_GADGET, // Search gadget when GD in sidebar mode.
GD_WEB_SERVER, // Boxes in web pages shown by local GD web server.
GD_OUTLOOK, // Search box installed within outlook by GD.
CHROME_OMNIBOX, // Chrome searches through the address bar omnibox.
CHROME_HOME_PAGE, // Chrome searches through Google as home page.
FFTB2_BOX, // Firefox Toolbar v2 Search Box.
FFTB3_BOX, // Firefox Toolbar v3+ Search Box.
PINYIN_IME_BHO, // Goopy Input Method Editor BHO (Pinyin).
IGOOGLE_WEBPAGE, // Searches on iGoogle through partner deals.
// Mobile idle screen search for different platforms.
MOBILE_IDLE_SCREEN_BLACKBERRY,
MOBILE_IDLE_SCREEN_WINMOB,
MOBILE_IDLE_SCREEN_SYMBIAN,
FF_HOME_PAGE, // Firefox home page when set to Google.
FF_SEARCH_BOX, // Firefox search box when set to Google.
IE_BROWSED_PAGE, // Search made in IE through user action (no product).
QSB_WIN_BOX, // Search box brought up by ctrl+space by default,
// distributed by toolbar and separate from the GD QSB
WEBAPPS_CALENDAR, // Webapps use of calendar.
WEBAPPS_DOCS, // Webapps use of writely.
WEBAPPS_GMAIL, // Webapps use of Gmail.
IETB_LINKDOCTOR, // Linkdoctor of IE Toolbar
FFTB_LINKDOCTOR, // Linkdoctor of FF Toolbar
IETB7_SEARCH_BOX, // IE Toolbar search box.
TB8_SEARCH_BOX, // IE/FF Toolbar search box.
CHROME_FRAME, // Chrome Frame.
// Partner access points.
PARTNER_AP_1,
PARTNER_AP_2,
PARTNER_AP_3,
PARTNER_AP_4,
PARTNER_AP_5,
// Unclaimed access points - should be used first before creating new APs.
// Please also make sure you re-name the enum before using an unclaimed value;
// this acts as a check to ensure we don't have collisions.
UNDEFINED_AP_H,
UNDEFINED_AP_I,
UNDEFINED_AP_J,
UNDEFINED_AP_K,
UNDEFINED_AP_L,
UNDEFINED_AP_M,
UNDEFINED_AP_N,
UNDEFINED_AP_O,
UNDEFINED_AP_P,
UNDEFINED_AP_Q,
UNDEFINED_AP_R,
UNDEFINED_AP_S,
UNDEFINED_AP_T,
UNDEFINED_AP_U,
UNDEFINED_AP_V,
UNDEFINED_AP_W,
UNDEFINED_AP_X,
UNDEFINED_AP_Y,
UNDEFINED_AP_Z,
PACK_AP0,
PACK_AP1,
PACK_AP2,
PACK_AP3,
PACK_AP4,
PACK_AP5,
PACK_AP6,
PACK_AP7,
PACK_AP8,
PACK_AP9,
PACK_AP10,
PACK_AP11,
PACK_AP12,
PACK_AP13,
// New Access Points should be added here without changing existing enums,
// (i.e. before LAST_ACCESS_POINT)
LAST_ACCESS_POINT
};
// A product is an entity which wants to gets credit for setting
// an Access Point.
enum Product {
IE_TOOLBAR = 1,
TOOLBAR_NOTIFIER,
PACK,
DESKTOP,
CHROME,
FF_TOOLBAR,
QSB_WIN,
WEBAPPS,
PINYIN_IME,
PARTNER
// New Products should be added here without changing existing enums.
};
// Events that note Product and Access Point modifications.
enum Event {
INVALID_EVENT = 0,
INSTALL = 1, // Access Point added to the system.
SET_TO_GOOGLE, // Point set from non-Google provider to Google.
FIRST_SEARCH, // First search from point since INSTALL
REPORT_RLS, // Report old system "RLS" financial value for this point.
// New Events should be added here without changing existing enums,
// before LAST_EVENT.
ACTIVATE, // Product being used for a period of time.
LAST_EVENT
};
} // namespace rlz_lib
#endif // RLZ_LIB_RLZ_ENUMS_H_

651
rlz/lib/rlz_lib.cc Normal file

@ -0,0 +1,651 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// A library to manage RLZ information for access-points shared
// across different client applications.
#include "rlz/lib/rlz_lib.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "rlz/lib/assert.h"
#include "rlz/lib/crc32.h"
#include "rlz/lib/financial_ping.h"
#include "rlz/lib/lib_values.h"
#include "rlz/lib/rlz_value_store.h"
#include "rlz/lib/string_utils.h"
namespace {
// Event information returned from ping response.
struct ReturnedEvent {
rlz_lib::AccessPoint access_point;
rlz_lib::Event event_type;
};
// Helper functions
bool IsAccessPointSupported(rlz_lib::AccessPoint point) {
switch (point) {
case rlz_lib::NO_ACCESS_POINT:
case rlz_lib::LAST_ACCESS_POINT:
case rlz_lib::MOBILE_IDLE_SCREEN_BLACKBERRY:
case rlz_lib::MOBILE_IDLE_SCREEN_WINMOB:
case rlz_lib::MOBILE_IDLE_SCREEN_SYMBIAN:
// These AP's are never available on Windows PCs.
return false;
case rlz_lib::IE_DEFAULT_SEARCH:
case rlz_lib::IE_HOME_PAGE:
case rlz_lib::IETB_SEARCH_BOX:
case rlz_lib::QUICK_SEARCH_BOX:
case rlz_lib::GD_DESKBAND:
case rlz_lib::GD_SEARCH_GADGET:
case rlz_lib::GD_WEB_SERVER:
case rlz_lib::GD_OUTLOOK:
case rlz_lib::CHROME_OMNIBOX:
case rlz_lib::CHROME_HOME_PAGE:
// TODO: Figure out when these settings are set to Google.
default:
return true;
}
}
// Current RLZ can only use [a-zA-Z0-9_\-]
// We will be more liberal and allow some additional chars, but not url meta
// chars.
bool IsGoodRlzChar(const char ch) {
if (IsAsciiAlpha(ch) || IsAsciiDigit(ch))
return true;
switch (ch) {
case '_':
case '-':
case '!':
case '@':
case '$':
case '*':
case '(':
case ')':
case ';':
case '.':
case '<':
case '>':
return true;
}
return false;
}
// This function will remove bad rlz chars and also limit the max rlz to some
// reasonable size. It also assumes that normalized_rlz is at least
// kMaxRlzLength+1 long.
void NormalizeRlz(const char* raw_rlz, char* normalized_rlz) {
int index = 0;
for (; raw_rlz[index] != 0 && index < rlz_lib::kMaxRlzLength; ++index) {
char current = raw_rlz[index];
if (IsGoodRlzChar(current)) {
normalized_rlz[index] = current;
} else {
normalized_rlz[index] = '.';
}
}
normalized_rlz[index] = 0;
}
void GetEventsFromResponseString(
const std::string& response_line,
const std::string& field_header,
std::vector<ReturnedEvent>* event_array) {
// Get the string of events.
std::string events = response_line.substr(field_header.size());
TrimWhitespaceASCII(events, TRIM_LEADING, &events);
int events_length = events.find_first_of("\r\n ");
if (events_length < 0)
events_length = events.size();
events = events.substr(0, events_length);
// Break this up into individual events
int event_end_index = -1;
do {
int event_begin = event_end_index + 1;
event_end_index = events.find(rlz_lib::kEventsCgiSeparator, event_begin);
int event_end = event_end_index;
if (event_end < 0)
event_end = events_length;
std::string event_string = events.substr(event_begin,
event_end - event_begin);
if (event_string.size() != 3) // 3 = 2(AP) + 1(E)
continue;
rlz_lib::AccessPoint point = rlz_lib::NO_ACCESS_POINT;
rlz_lib::Event event = rlz_lib::INVALID_EVENT;
if (!GetAccessPointFromName(event_string.substr(0, 2).c_str(), &point) ||
point == rlz_lib::NO_ACCESS_POINT) {
continue;
}
if (!GetEventFromName(event_string.substr(event_string.size() - 1).c_str(),
&event) || event == rlz_lib::INVALID_EVENT) {
continue;
}
ReturnedEvent current_event = {point, event};
event_array->push_back(current_event);
} while (event_end_index >= 0);
}
// Event storage functions.
bool RecordStatefulEvent(rlz_lib::Product product, rlz_lib::AccessPoint point,
rlz_lib::Event event) {
rlz_lib::ScopedRlzValueStoreLock lock;
rlz_lib::RlzValueStore* store = lock.GetStore();
if (!store || !store->HasAccess(rlz_lib::RlzValueStore::kWriteAccess))
return false;
// Write the new event to the value store.
const char* point_name = GetAccessPointName(point);
const char* event_name = GetEventName(event);
if (!point_name || !event_name)
return false;
if (!point_name[0] || !event_name[0])
return false;
std::string new_event_value;
base::StringAppendF(&new_event_value, "%s%s", point_name, event_name);
return store->AddStatefulEvent(product, new_event_value.c_str());
}
bool GetProductEventsAsCgiHelper(rlz_lib::Product product, char* cgi,
size_t cgi_size,
rlz_lib::RlzValueStore* store) {
// Prepend the CGI param key to the buffer.
std::string cgi_arg;
base::StringAppendF(&cgi_arg, "%s=", rlz_lib::kEventsCgiVariable);
if (cgi_size <= cgi_arg.size())
return false;
size_t index;
for (index = 0; index < cgi_arg.size(); ++index)
cgi[index] = cgi_arg[index];
// Read stored events.
std::vector<std::string> events;
if (!store->ReadProductEvents(product, &events))
return false;
// Append the events to the buffer.
size_t num_values = 0;
for (num_values = 0; num_values < events.size(); ++num_values) {
cgi[index] = '\0';
int divider = num_values > 0 ? 1 : 0;
int size = cgi_size - (index + divider);
if (size <= 0)
return cgi_size >= (rlz_lib::kMaxCgiLength + 1);
strncpy(cgi + index + divider, events[num_values].c_str(), size);
if (divider)
cgi[index] = rlz_lib::kEventsCgiSeparator;
index += std::min((int)events[num_values].length(), size) + divider;
}
cgi[index] = '\0';
return num_values > 0;
}
} // namespace
namespace rlz_lib {
#if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
bool SetURLRequestContext(net::URLRequestContextGetter* context) {
return FinancialPing::SetURLRequestContext(context);
}
#endif
bool GetProductEventsAsCgi(Product product, char* cgi, size_t cgi_size) {
if (!cgi || cgi_size <= 0) {
ASSERT_STRING("GetProductEventsAsCgi: Invalid buffer");
return false;
}
cgi[0] = 0;
ScopedRlzValueStoreLock lock;
RlzValueStore* store = lock.GetStore();
if (!store || !store->HasAccess(RlzValueStore::kReadAccess))
return false;
size_t size_local = std::min(
static_cast<size_t>(kMaxCgiLength + 1), cgi_size);
bool result = GetProductEventsAsCgiHelper(product, cgi, size_local, store);
if (!result) {
ASSERT_STRING("GetProductEventsAsCgi: Possibly insufficient buffer size");
cgi[0] = 0;
return false;
}
return true;
}
bool RecordProductEvent(Product product, AccessPoint point, Event event) {
ScopedRlzValueStoreLock lock;
RlzValueStore* store = lock.GetStore();
if (!store || !store->HasAccess(RlzValueStore::kWriteAccess))
return false;
// Get this event's value.
const char* point_name = GetAccessPointName(point);
const char* event_name = GetEventName(event);
if (!point_name || !event_name)
return false;
if (!point_name[0] || !event_name[0])
return false;
std::string new_event_value;
base::StringAppendF(&new_event_value, "%s%s", point_name, event_name);
// Check whether this event is a stateful event. If so, don't record it.
if (store->IsStatefulEvent(product, new_event_value.c_str())) {
// For a stateful event we skip recording, this function is also
// considered successful.
return true;
}
// Write the new event to the value store.
return store->AddProductEvent(product, new_event_value.c_str());
}
bool ClearProductEvent(Product product, AccessPoint point, Event event) {
ScopedRlzValueStoreLock lock;
RlzValueStore* store = lock.GetStore();
if (!store || !store->HasAccess(RlzValueStore::kWriteAccess))
return false;
// Get the event's value store value and delete it.
const char* point_name = GetAccessPointName(point);
const char* event_name = GetEventName(event);
if (!point_name || !event_name)
return false;
if (!point_name[0] || !event_name[0])
return false;
std::string event_value;
base::StringAppendF(&event_value, "%s%s", point_name, event_name);
return store->ClearProductEvent(product, event_value.c_str());
}
// RLZ storage functions.
bool GetAccessPointRlz(AccessPoint point, char* rlz, size_t rlz_size) {
if (!rlz || rlz_size <= 0) {
ASSERT_STRING("GetAccessPointRlz: Invalid buffer");
return false;
}
rlz[0] = 0;
ScopedRlzValueStoreLock lock;
RlzValueStore* store = lock.GetStore();
if (!store || !store->HasAccess(RlzValueStore::kReadAccess))
return false;
if (!IsAccessPointSupported(point))
return false;
return store->ReadAccessPointRlz(point, rlz, rlz_size);
}
bool SetAccessPointRlz(AccessPoint point, const char* new_rlz) {
ScopedRlzValueStoreLock lock;
RlzValueStore* store = lock.GetStore();
if (!store || !store->HasAccess(RlzValueStore::kWriteAccess))
return false;
if (!new_rlz) {
ASSERT_STRING("SetAccessPointRlz: Invalid buffer");
return false;
}
// Return false if the access point is not set to Google.
if (!IsAccessPointSupported(point)) {
ASSERT_STRING(("SetAccessPointRlz: "
"Cannot set RLZ for unsupported access point."));
return false;
}
// Verify the RLZ length.
size_t rlz_length = strlen(new_rlz);
if (rlz_length > kMaxRlzLength) {
ASSERT_STRING("SetAccessPointRlz: RLZ length is exceeds max allowed.");
return false;
}
char normalized_rlz[kMaxRlzLength + 1];
NormalizeRlz(new_rlz, normalized_rlz);
VERIFY(strlen(new_rlz) == rlz_length);
// Setting RLZ to empty == clearing.
if (normalized_rlz[0] == 0)
return store->ClearAccessPointRlz(point);
return store->WriteAccessPointRlz(point, normalized_rlz);
}
// Financial Server pinging functions.
bool FormFinancialPingRequest(Product product, const AccessPoint* access_points,
const char* product_signature,
const char* product_brand,
const char* product_id,
const char* product_lang,
bool exclude_machine_id,
char* request, size_t request_buffer_size) {
if (!request || request_buffer_size == 0)
return false;
request[0] = 0;
std::string request_string;
if (!FinancialPing::FormRequest(product, access_points, product_signature,
product_brand, product_id, product_lang,
exclude_machine_id, &request_string))
return false;
if (request_string.size() >= request_buffer_size)
return false;
strncpy(request, request_string.c_str(), request_buffer_size);
request[request_buffer_size - 1] = 0;
return true;
}
bool PingFinancialServer(Product product, const char* request, char* response,
size_t response_buffer_size) {
if (!response || response_buffer_size == 0)
return false;
response[0] = 0;
// Check if the time is right to ping.
if (!FinancialPing::IsPingTime(product, false))
return false;
// Send out the ping.
std::string response_string;
if (!FinancialPing::PingServer(request, &response_string))
return false;
if (response_string.size() >= response_buffer_size)
return false;
strncpy(response, response_string.c_str(), response_buffer_size);
response[response_buffer_size - 1] = 0;
return true;
}
bool IsPingResponseValid(const char* response, int* checksum_idx) {
if (!response || !response[0])
return false;
if (checksum_idx)
*checksum_idx = -1;
if (strlen(response) > kMaxPingResponseLength) {
ASSERT_STRING("IsPingResponseValid: response is too long to parse.");
return false;
}
// Find the checksum line.
std::string response_string(response);
std::string checksum_param("\ncrc32: ");
int calculated_crc;
int checksum_index = response_string.find(checksum_param);
if (checksum_index >= 0) {
// Calculate checksum of message preceeding checksum line.
// (+ 1 to include the \n)
std::string message(response_string.substr(0, checksum_index + 1));
if (!Crc32(message.c_str(), &calculated_crc))
return false;
} else {
checksum_param = "crc32: "; // Empty response case.
if (!StartsWithASCII(response_string, checksum_param, true))
return false;
checksum_index = 0;
if (!Crc32("", &calculated_crc))
return false;
}
// Find the checksum value on the response.
int checksum_end = response_string.find("\n", checksum_index + 1);
if (checksum_end < 0)
checksum_end = response_string.size();
int checksum_begin = checksum_index + checksum_param.size();
std::string checksum = response_string.substr(checksum_begin,
checksum_end - checksum_begin + 1);
TrimWhitespaceASCII(checksum, TRIM_ALL, &checksum);
if (checksum_idx)
*checksum_idx = checksum_index;
return calculated_crc == HexStringToInteger(checksum.c_str());
}
// Complex helpers built on top of other functions.
bool ParseFinancialPingResponse(Product product, const char* response) {
// Update the last ping time irrespective of success.
FinancialPing::UpdateLastPingTime(product);
// Parse the ping response - update RLZs, clear events.
return ParsePingResponse(product, response);
}
bool SendFinancialPing(Product product, const AccessPoint* access_points,
const char* product_signature,
const char* product_brand,
const char* product_id, const char* product_lang,
bool exclude_machine_id) {
return SendFinancialPing(product, access_points, product_signature,
product_brand, product_id, product_lang,
exclude_machine_id, false);
}
bool SendFinancialPing(Product product, const AccessPoint* access_points,
const char* product_signature,
const char* product_brand,
const char* product_id, const char* product_lang,
bool exclude_machine_id,
const bool skip_time_check) {
// Create the financial ping request.
std::string request;
if (!FinancialPing::FormRequest(product, access_points, product_signature,
product_brand, product_id, product_lang,
exclude_machine_id, &request))
return false;
// Check if the time is right to ping.
if (!FinancialPing::IsPingTime(product, skip_time_check))
return false;
// Send out the ping, update the last ping time irrespective of success.
FinancialPing::UpdateLastPingTime(product);
std::string response;
if (!FinancialPing::PingServer(request.c_str(), &response))
return false;
// Parse the ping response - update RLZs, clear events.
return ParsePingResponse(product, response.c_str());
}
// TODO: Use something like RSA to make sure the response is
// from a Google server.
bool ParsePingResponse(Product product, const char* response) {
rlz_lib::ScopedRlzValueStoreLock lock;
rlz_lib::RlzValueStore* store = lock.GetStore();
if (!store || !store->HasAccess(rlz_lib::RlzValueStore::kWriteAccess))
return false;
std::string response_string(response);
int response_length = -1;
if (!IsPingResponseValid(response, &response_length))
return false;
if (0 == response_length)
return true; // Empty response - no parsing.
std::string events_variable;
std::string stateful_events_variable;
base::SStringPrintf(&events_variable, "%s: ", kEventsCgiVariable);
base::SStringPrintf(&stateful_events_variable, "%s: ",
kStatefulEventsCgiVariable);
int rlz_cgi_length = strlen(kRlzCgiVariable);
// Split response lines. Expected response format is lines of the form:
// rlzW1: 1R1_____en__252
int line_end_index = -1;
do {
int line_begin = line_end_index + 1;
line_end_index = response_string.find("\n", line_begin);
int line_end = line_end_index;
if (line_end < 0)
line_end = response_length;
if (line_end <= line_begin)
continue; // Empty line.
std::string response_line;
response_line = response_string.substr(line_begin, line_end - line_begin);
if (StartsWithASCII(response_line, kRlzCgiVariable, true)) { // An RLZ.
int separator_index = -1;
if ((separator_index = response_line.find(": ")) < 0)
continue; // Not a valid key-value pair.
// Get the access point.
std::string point_name =
response_line.substr(3, separator_index - rlz_cgi_length);
AccessPoint point = NO_ACCESS_POINT;
if (!GetAccessPointFromName(point_name.c_str(), &point) ||
point == NO_ACCESS_POINT)
continue; // Not a valid access point.
// Get the new RLZ.
std::string rlz_value(response_line.substr(separator_index + 2));
TrimWhitespaceASCII(rlz_value, TRIM_LEADING, &rlz_value);
int rlz_length = rlz_value.find_first_of("\r\n ");
if (rlz_length < 0)
rlz_length = rlz_value.size();
if (rlz_length > kMaxRlzLength)
continue; // Too long.
if (IsAccessPointSupported(point))
SetAccessPointRlz(point, rlz_value.substr(0, rlz_length).c_str());
} else if (StartsWithASCII(response_line, events_variable, true)) {
// Clear events which server parsed.
std::vector<ReturnedEvent> event_array;
GetEventsFromResponseString(response_line, events_variable, &event_array);
for (size_t i = 0; i < event_array.size(); ++i) {
ClearProductEvent(product, event_array[i].access_point,
event_array[i].event_type);
}
} else if (StartsWithASCII(response_line, stateful_events_variable, true)) {
// Record any stateful events the server send over.
std::vector<ReturnedEvent> event_array;
GetEventsFromResponseString(response_line, stateful_events_variable,
&event_array);
for (size_t i = 0; i < event_array.size(); ++i) {
RecordStatefulEvent(product, event_array[i].access_point,
event_array[i].event_type);
}
}
} while (line_end_index >= 0);
#if defined(OS_WIN)
// Update the DCC in registry if needed.
SetMachineDealCodeFromPingResponse(response);
#endif
return true;
}
bool GetPingParams(Product product, const AccessPoint* access_points,
char* cgi, size_t cgi_size) {
if (!cgi || cgi_size <= 0) {
ASSERT_STRING("GetPingParams: Invalid buffer");
return false;
}
cgi[0] = 0;
if (!access_points) {
ASSERT_STRING("GetPingParams: access_points is NULL");
return false;
}
// Add the RLZ Exchange Protocol version.
std::string cgi_string(kProtocolCgiArgument);
// Copy the &rlz= over.
base::StringAppendF(&cgi_string, "&%s=", kRlzCgiVariable);
{
// Now add each of the RLZ's. Keep the lock during all GetAccessPointRlz()
// calls below.
ScopedRlzValueStoreLock lock;
RlzValueStore* store = lock.GetStore();
if (!store || !store->HasAccess(RlzValueStore::kReadAccess))
return false;
bool first_rlz = true; // comma before every RLZ but the first.
for (int i = 0; access_points[i] != NO_ACCESS_POINT; i++) {
char rlz[kMaxRlzLength + 1];
if (GetAccessPointRlz(access_points[i], rlz, arraysize(rlz))) {
const char* access_point = GetAccessPointName(access_points[i]);
if (!access_point)
continue;
base::StringAppendF(&cgi_string, "%s%s%s%s",
first_rlz ? "" : kRlzCgiSeparator,
access_point, kRlzCgiIndicator, rlz);
first_rlz = false;
}
}
#if defined(OS_WIN)
// Report the DCC too if not empty. DCCs are windows-only.
char dcc[kMaxDccLength + 1];
dcc[0] = 0;
if (GetMachineDealCode(dcc, arraysize(dcc)) && dcc[0])
base::StringAppendF(&cgi_string, "&%s=%s", kDccCgiVariable, dcc);
#endif
}
if (cgi_string.size() >= cgi_size)
return false;
strncpy(cgi, cgi_string.c_str(), cgi_size);
cgi[cgi_size - 1] = 0;
return true;
}
} // namespace rlz_lib

335
rlz/lib/rlz_lib.h Normal file

@ -0,0 +1,335 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// A library to manage RLZ information for access-points shared
// across different client applications.
//
// All functions return true on success and false on error.
// This implemenation is thread safe.
#ifndef RLZ_LIB_RLZ_LIB_H_
#define RLZ_LIB_RLZ_LIB_H_
#include <stdio.h>
#include <string>
#include "build/build_config.h"
#include "rlz/lib/rlz_enums.h"
#if defined(OS_WIN)
#define RLZ_LIB_API __cdecl
#else
#define RLZ_LIB_API
#endif
// Define one of
// + RLZ_NETWORK_IMPLEMENTATION_WIN_INET: Uses win inet to send financial pings.
// + RLZ_NETWORK_IMPLEMENTATION_CHROME_NET: Uses chrome's network stack to send
// financial pings. rlz_lib::SetURLRequestContext() must be called before
// any calls to SendFinancialPing().
#if defined(RLZ_NETWORK_IMPLEMENTATION_WIN_INET) && \
defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
#error Exactly one of RLZ_NETWORK_IMPLEMENTATION_WIN_INET and \
RLZ_NETWORK_IMPLEMENTATION_CHROME_NET should be defined.
#endif
#if !defined(RLZ_NETWORK_IMPLEMENTATION_WIN_INET) && \
!defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
#if defined(OS_WIN)
#define RLZ_NETWORK_IMPLEMENTATION_WIN_INET
#else
#define RLZ_NETWORK_IMPLEMENTATION_CHROME_NET
#endif
#endif
#if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
namespace net {
class URLRequestContextGetter;
} // namespace net
#endif
namespace rlz_lib {
class ScopedRlzValueStoreLock;
// The maximum length of an access points RLZ in bytes.
const int kMaxRlzLength = 64;
// The maximum length of an access points RLZ in bytes.
const int kMaxDccLength = 128;
// The maximum length of a CGI string in bytes.
const int kMaxCgiLength = 2048;
// The maximum length of a ping response we will parse in bytes. If the response
// is bigger, please break it up into separate calls.
const int kMaxPingResponseLength = 0x4000; // 16K
#if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
// Set the URLRequestContextGetter used by SendFinancialPing(). The IO message
// loop returned by this context will be used for the IO done by
// SendFinancialPing().
bool RLZ_LIB_API SetURLRequestContext(net::URLRequestContextGetter* context);
#endif
// RLZ storage functions.
// Get all the events reported by this product as a CGI string to append to
// the daily ping.
// Access: HKCU read.
bool RLZ_LIB_API GetProductEventsAsCgi(Product product, char* unescaped_cgi,
size_t unescaped_cgi_size);
// Records an RLZ event.
// Some events can be product-independent (e.g: First search from home page),
// and some can be access point independent (e.g. Pack installed). However,
// product independent events must still include the product which cares about
// that information being reported.
// Access: HKCU write.
bool RLZ_LIB_API RecordProductEvent(Product product, AccessPoint point,
Event event_id);
// Clear an event reported by this product. This should be called after a
// successful ping to the RLZ server.
// Access: HKCU write.
bool RLZ_LIB_API ClearProductEvent(Product product, AccessPoint point,
Event event_id);
// Clear all reported events and recorded stateful events of this product.
// This should be called on complete uninstallation of the product.
// Access: HKCU write.
bool RLZ_LIB_API ClearAllProductEvents(Product product);
// Clears all product-specifc state from the RLZ registry.
// Should be called during product uninstallation.
// This removes outstanding product events, product financial ping times,
// the product RLS argument (if any), and any RLZ's for access points being
// uninstalled with the product.
// access_points is an array terminated with NO_ACCESS_POINT.
// IMPORTANT: These are the access_points the product is removing as part
// of the uninstallation, not necessarily all the access points passed to
// SendFinancialPing() and GetPingParams().
// access_points can be NULL if no points are being uninstalled.
// No return value - this is best effort. Will assert in debug mode on
// failed attempts.
// Access: HKCU write.
void RLZ_LIB_API ClearProductState(Product product,
const AccessPoint* access_points);
// Get the RLZ value of the access point. If the access point is not Google, the
// RLZ will be the empty string and the function will return false.
// Access: HKCU read.
bool RLZ_LIB_API GetAccessPointRlz(AccessPoint point, char* rlz,
size_t rlz_size);
// Set the RLZ for the access-point. Fails and asserts if called when the access
// point is not set to Google.
// new_rlz should come from a server-response. Client applications should not
// create their own RLZ values.
// Access: HKCU write.
bool RLZ_LIB_API SetAccessPointRlz(AccessPoint point, const char* new_rlz);
// Financial Server pinging functions.
// These functions deal with pinging the RLZ financial server and parsing and
// acting upon the response. Clients should SendFinancialPing() to avoid needing
// these functions. However, these functions allow clients to split the various
// parts of the pinging process up as needed (to avoid firewalls, etc).
// Forms the HTTP request to send to the RLZ financial server.
//
// product : The product to ping for.
// access_points : The access points this product affects. Array must be
// terminated with NO_ACCESS_POINT.
// product_signature : The signature sent with daily pings (e.g. swg, ietb)
// product_brand : The brand of the pinging product, if any.
// product_id : The product-specific installation ID (can be NULL).
// product_lang : The language for the product (used to determine cohort).
// exclude_machine_id : Whether the Machine ID should be explicitly excluded
// based on the products privacy policy.
// request : The buffer where the function returns the HTTP request.
// request_buffer_size: The size of the request buffer in bytes. The buffer
// size (kMaxCgiLength+1) is guaranteed to be enough.
//
// Access: HKCU read.
bool RLZ_LIB_API FormFinancialPingRequest(Product product,
const AccessPoint* access_points,
const char* product_signature,
const char* product_brand,
const char* product_id,
const char* product_lang,
bool exclude_machine_id,
char* request,
size_t request_buffer_size);
// Pings the financial server and returns the HTTP response. This will fail
// if it is too early to ping the server since the last ping.
//
// If RLZ_NETWORK_IMPLEMENTATION_CHROME_NET is set, SetURLRequestContext() needs
// to be called before calling this function.
//
// product : The product to ping for.
// request : The HTTP request (for example, returned by
// FormFinancialPingRequest).
// response : The buffer in which the HTTP response is returned.
// response_buffer_size : The size of the response buffer in bytes. The buffer
// size (kMaxPingResponseLength+1) is enough for all
// legitimate server responses (any response that is
// bigger should be considered the same way as a general
// network problem).
//
// Access: HKCU read.
bool RLZ_LIB_API PingFinancialServer(Product product,
const char* request,
char* response,
size_t response_buffer_size);
// Checks if a ping response is valid - ie. it has a checksum line which
// is the CRC-32 checksum of the message uptil the checksum. If
// checksum_idx is not NULL, it will get the index of the checksum, i.e. -
// the effective end of the message.
// Access: No restrictions.
bool RLZ_LIB_API IsPingResponseValid(const char* response,
int* checksum_idx);
// Complex helpers built on top of other functions.
// Parses the responses from the financial server and updates product state
// and access point RLZ's in registry. Like ParsePingResponse(), but also
// updates the last ping time.
// Access: HKCU write.
bool RLZ_LIB_API ParseFinancialPingResponse(Product product,
const char* response);
// Send the ping with RLZs and events to the PSO server.
// This ping method should be called daily. (More frequent calls will fail).
// Also, if there are no events, the call will succeed only once a week.
//
// If RLZ_NETWORK_IMPLEMENTATION_CHROME_NET is set, SetURLRequestContext() needs
// to be called before calling this function.
//
// product : The product to ping for.
// access_points : The access points this product affects. Array must be
// terminated with NO_ACCESS_POINT.
// product_signature : The signature sent with daily pings (e.g. swg, ietb)
// product_brand : The brand of the pinging product, if any.
// product_id : The product-specific installation ID (can be NULL).
// product_lang : The language for the product (used to determine cohort).
// exclude_machine_id : Whether the Machine ID should be explicitly excluded
// based on the products privacy policy.
//
// Returns true on successful ping and response, false otherwise.
// Access: HKCU write.
bool RLZ_LIB_API SendFinancialPing(Product product,
const AccessPoint* access_points,
const char* product_signature,
const char* product_brand,
const char* product_id,
const char* product_lang,
bool exclude_machine_id);
// An alternate implementations of SendFinancialPing with the same behavior,
// except the caller can optionally choose to skip the timing check.
bool RLZ_LIB_API SendFinancialPing(Product product,
const AccessPoint* access_points,
const char* product_signature,
const char* product_brand,
const char* product_id,
const char* product_lang,
bool exclude_machine_id,
const bool skip_time_check);
// Parses RLZ related ping response information from the server.
// Updates stored RLZ values and clears stored events accordingly.
// Access: HKCU write.
bool RLZ_LIB_API ParsePingResponse(Product product, const char* response);
// Copies the events associated with the product and the RLZ's for each access
// point in access_points into cgi. This string can be directly appended
// to a ping (will need an & if not first paramter).
// access_points must be an array of AccessPoints terminated with
// NO_ACCESS_POINT.
// Access: HKCU read.
bool RLZ_LIB_API GetPingParams(Product product,
const AccessPoint* access_points,
char* unescaped_cgi, size_t unescaped_cgi_size);
#if defined(OS_WIN)
// OEM Deal confirmation storage functions. OEM Deals are windows-only.
// Makes the OEM Deal Confirmation code writable by all users on the machine.
// This should be called before calling SetMachineDealCode from a non-admin
// account.
// Access: HKLM write.
bool RLZ_LIB_API CreateMachineState(void);
// Set the OEM Deal Confirmation Code (DCC). This information is used for RLZ
// initalization.
// Access: HKLM write, or
// HKCU read if rlz_lib::CreateMachineState() has been sucessfully called.
bool RLZ_LIB_API SetMachineDealCode(const char* dcc);
// Get the DCC cgi argument string to append to a daily ping.
// Should be used only by OEM deal trackers. Applications should use the
// GetMachineDealCode method which has an AccessPoint paramter.
// Access: HKLM read.
bool RLZ_LIB_API GetMachineDealCodeAsCgi(char* cgi, size_t cgi_size);
// Get the DCC value stored in registry.
// Should be used only by OEM deal trackers. Applications should use the
// GetMachineDealCode method which has an AccessPoint paramter.
// Access: HKLM read.
bool RLZ_LIB_API GetMachineDealCode(char* dcc, size_t dcc_size);
// Parses a ping response, checks if it is valid and sets the machine DCC
// from the response. The ping must also contain the current DCC value in
// order to be considered valid.
// Access: HKLM write;
// HKCU write if CreateMachineState() has been successfully called.
bool RLZ_LIB_API SetMachineDealCodeFromPingResponse(const char* response);
#endif
// Segment RLZ persistence based on branding information.
// All information for a given product is persisted under keys with the either
// product's name or its access point's name. This assumes that only
// one instance of the product is installed on the machine, and that only one
// product brand is associated with it.
//
// In some cases, a given product may be using supplementary brands. The RLZ
// information must be kept separately for each of these brands. To achieve
// this segmentation, scope all RLZ library calls that deal with supplementary
// brands within the lifetime of an rlz_lib::ProductBranding instance.
//
// For example, to record events for a supplementary brand, do the following:
//
// {
// rlz_lib::SupplementaryBranding branding("AAAA");
// // This call to RecordProductEvent is scoped to the AAAA brand.
// rlz_lib::RecordProductEvent(rlz_lib::DESKTOP, rlz_lib::GD_DESKBAND,
// rlz_lib::INSTALL);
// }
//
// // This call to RecordProductEvent is not scoped to any supplementary brand.
// rlz_lib::RecordProductEvent(rlz_lib::DESKTOP, rlz_lib::GD_DESKBAND,
// rlz_lib::INSTALL);
//
// In particular, this affects the recording of stateful events and the sending
// of financial pings. In the former case, a stateful event recorded while
// scoped to a supplementary brand will be recorded again when scoped to a
// different supplementary brand (or not scoped at all). In the latter case,
// the time skip check is specific to each supplementary brand.
class SupplementaryBranding {
public:
SupplementaryBranding(const char* brand);
~SupplementaryBranding();
static const std::string& GetBrand();
private:
ScopedRlzValueStoreLock* lock_;
};
} // namespace rlz_lib
#endif // RLZ_LIB_RLZ_LIB_H_

82
rlz/lib/rlz_lib_clear.cc Normal file

@ -0,0 +1,82 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// The methods in this file belong conceptually to rlz_lib.cc. However, some
// programs depend on rlz only to call ClearAllProductEvents(), so this file
// contains this in fairly self-contained form to make it easier for linkers
// to strip away most of rlz. In particular, this file should not reference any
// symbols defined in financial_ping.cc.
#include "rlz/lib/rlz_lib.h"
#include "base/lazy_instance.h"
#include "rlz/lib/assert.h"
#include "rlz/lib/rlz_value_store.h"
namespace rlz_lib {
bool ClearAllProductEvents(Product product) {
rlz_lib::ScopedRlzValueStoreLock lock;
rlz_lib::RlzValueStore* store = lock.GetStore();
if (!store || !store->HasAccess(rlz_lib::RlzValueStore::kWriteAccess))
return false;
bool result;
result = store->ClearAllProductEvents(product);
result &= store->ClearAllStatefulEvents(product);
return result;
}
void ClearProductState(Product product, const AccessPoint* access_points) {
rlz_lib::ScopedRlzValueStoreLock lock;
rlz_lib::RlzValueStore* store = lock.GetStore();
if (!store || !store->HasAccess(rlz_lib::RlzValueStore::kWriteAccess))
return;
// Delete all product specific state.
VERIFY(ClearAllProductEvents(product));
VERIFY(store->ClearPingTime(product));
// Delete all RLZ's for access points being uninstalled.
if (access_points) {
for (int i = 0; access_points[i] != NO_ACCESS_POINT; i++) {
VERIFY(store->ClearAccessPointRlz(access_points[i]));
}
}
store->CollectGarbage();
}
static base::LazyInstance<std::string>::Leaky g_supplemental_branding;
SupplementaryBranding::SupplementaryBranding(const char* brand)
: lock_(new ScopedRlzValueStoreLock) {
if (!lock_->GetStore())
return;
if (!g_supplemental_branding.Get().empty()) {
ASSERT_STRING("ProductBranding: existing brand is not empty");
return;
}
if (brand == NULL || brand[0] == 0) {
ASSERT_STRING("ProductBranding: new brand is empty");
return;
}
g_supplemental_branding.Get() = brand;
}
SupplementaryBranding::~SupplementaryBranding() {
if (lock_->GetStore())
g_supplemental_branding.Get().clear();
delete lock_;
}
// static
const std::string& SupplementaryBranding::GetBrand() {
return g_supplemental_branding.Get();
}
} // namespace rlz_lib

824
rlz/lib/rlz_lib_test.cc Normal file

@ -0,0 +1,824 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// A test application for the RLZ library.
//
// These tests should not be executed on the build server:
// - They assert for the failed cases.
// - They modify machine state (registry).
//
// These tests require write access to HKLM and HKCU.
//
// The "GGLA" brand is used to test the normal code flow of the code, and the
// "TEST" brand is used to test the supplementary brand code code flow.
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "rlz/lib/rlz_lib.h"
#include "rlz/test/rlz_test_helpers.h"
#if defined(OS_WIN)
#include <Windows.h>
#include "rlz/win/lib/machine_deal.h"
#endif
#if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
#include "base/mac/scoped_nsautorelease_pool.h"
#include "base/threading/thread.h"
#include "net/url_request/url_request_test_util.h"
#endif
class MachineDealCodeHelper
#if defined(OS_WIN)
: public rlz_lib::MachineDealCode
#endif
{
public:
static bool Clear() {
#if defined(OS_WIN)
return rlz_lib::MachineDealCode::Clear();
#else
return true;
#endif
}
private:
MachineDealCodeHelper() {}
~MachineDealCodeHelper() {}
};
class RlzLibTest : public RlzLibTestBase {
};
TEST_F(RlzLibTest, RecordProductEvent) {
char cgi_50[50];
EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
cgi_50, 50));
EXPECT_STREQ("events=I7S", cgi_50);
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
cgi_50, 50));
EXPECT_STREQ("events=I7S,W1I", cgi_50);
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
cgi_50, 50));
EXPECT_STREQ("events=I7S,W1I", cgi_50);
}
TEST_F(RlzLibTest, ClearProductEvent) {
char cgi_50[50];
// Clear 1 of 1 events.
EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
cgi_50, 50));
EXPECT_STREQ("events=I7S", cgi_50);
EXPECT_TRUE(rlz_lib::ClearProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
EXPECT_FALSE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
cgi_50, 50));
EXPECT_STREQ("", cgi_50);
// Clear 1 of 2 events.
EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
cgi_50, 50));
EXPECT_STREQ("events=I7S,W1I", cgi_50);
EXPECT_TRUE(rlz_lib::ClearProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
cgi_50, 50));
EXPECT_STREQ("events=W1I", cgi_50);
// Clear a non-recorded event.
EXPECT_TRUE(rlz_lib::ClearProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IETB_SEARCH_BOX, rlz_lib::FIRST_SEARCH));
EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
cgi_50, 50));
EXPECT_STREQ("events=W1I", cgi_50);
}
TEST_F(RlzLibTest, GetProductEventsAsCgi) {
char cgi_50[50];
char cgi_1[1];
EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
cgi_50, 50));
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
EXPECT_FALSE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
cgi_1, 1));
EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
cgi_50, 50));
EXPECT_STREQ("events=I7S,W1I", cgi_50);
}
TEST_F(RlzLibTest, ClearAllAllProductEvents) {
char cgi_50[50];
EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
cgi_50, 50));
EXPECT_STREQ("events=I7S", cgi_50);
EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
EXPECT_FALSE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
cgi_50, 50));
EXPECT_STREQ("", cgi_50);
}
TEST_F(RlzLibTest, SetAccessPointRlz) {
char rlz_50[50];
EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, ""));
EXPECT_TRUE(rlz_lib::GetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, rlz_50, 50));
EXPECT_STREQ("", rlz_50);
EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, "IeTbRlz"));
EXPECT_TRUE(rlz_lib::GetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, rlz_50, 50));
EXPECT_STREQ("IeTbRlz", rlz_50);
}
TEST_F(RlzLibTest, GetAccessPointRlz) {
char rlz_1[1];
char rlz_50[50];
EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, ""));
EXPECT_TRUE(rlz_lib::GetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, rlz_1, 1));
EXPECT_STREQ("", rlz_1);
EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, "IeTbRlz"));
EXPECT_FALSE(rlz_lib::GetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, rlz_1, 1));
EXPECT_TRUE(rlz_lib::GetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, rlz_50, 50));
EXPECT_STREQ("IeTbRlz", rlz_50);
}
TEST_F(RlzLibTest, GetPingParams) {
MachineDealCodeHelper::Clear();
EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX,
"TbRlzValue"));
EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IE_HOME_PAGE, ""));
char cgi[2048];
rlz_lib::AccessPoint points[] =
{rlz_lib::IETB_SEARCH_BOX, rlz_lib::NO_ACCESS_POINT,
rlz_lib::NO_ACCESS_POINT};
EXPECT_TRUE(rlz_lib::GetPingParams(rlz_lib::TOOLBAR_NOTIFIER, points,
cgi, 2048));
EXPECT_STREQ("rep=2&rlz=T4:TbRlzValue", cgi);
#if defined(OS_WIN)
EXPECT_TRUE(rlz_lib::MachineDealCode::Set("dcc_value"));
#define DCC_PARAM "&dcc=dcc_value"
#else
#define DCC_PARAM ""
#endif
EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, ""));
EXPECT_TRUE(rlz_lib::GetPingParams(rlz_lib::TOOLBAR_NOTIFIER, points,
cgi, 2048));
EXPECT_STREQ("rep=2&rlz=T4:" DCC_PARAM, cgi);
EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX,
"TbRlzValue"));
EXPECT_FALSE(rlz_lib::GetPingParams(rlz_lib::TOOLBAR_NOTIFIER, points,
cgi, 23 + strlen(DCC_PARAM)));
EXPECT_STREQ("", cgi);
EXPECT_TRUE(rlz_lib::GetPingParams(rlz_lib::TOOLBAR_NOTIFIER, points,
cgi, 24 + strlen(DCC_PARAM)));
EXPECT_STREQ("rep=2&rlz=T4:TbRlzValue" DCC_PARAM, cgi);
EXPECT_TRUE(GetAccessPointRlz(rlz_lib::IE_HOME_PAGE, cgi, 2048));
points[2] = rlz_lib::IE_HOME_PAGE;
EXPECT_TRUE(rlz_lib::GetPingParams(rlz_lib::TOOLBAR_NOTIFIER, points,
cgi, 2048));
EXPECT_STREQ("rep=2&rlz=T4:TbRlzValue" DCC_PARAM, cgi);
}
TEST_F(RlzLibTest, IsPingResponseValid) {
const char* kBadPingResponses[] = {
// No checksum.
"version: 3.0.914.7250\r\n"
"url: http://www.corp.google.com/~av/45/opt/SearchWithGoogleUpdate.exe\r\n"
"launch-action: custom-action\r\n"
"launch-target: SearchWithGoogleUpdate.exe\r\n"
"signature: c08a3f4438e1442c4fe5678ee147cf6c5516e5d62bb64e\r\n"
"rlz: 1R1_____en__252\r\n"
"rlzXX: 1R1_____en__250\r\n",
// Invalid checksum.
"version: 3.0.914.7250\r\n"
"url: http://www.corp.google.com/~av/45/opt/SearchWithGoogleUpdate.exe\r\n"
"launch-action: custom-action\r\n"
"launch-target: SearchWithGoogleUpdate.exe\r\n"
"signature: c08a3f4438e1442c4fe5678ee147cf6c5516e5d62bb64e\r\n"
"rlz: 1R1_____en__252\r\n"
"rlzXX: 1R1_____en__250\r\n"
"rlzT4 1T4_____en__251\r\n"
"rlzT4: 1T4_____en__252\r\n"
"rlz\r\n"
"crc32: B12CC79A",
// Misplaced checksum.
"version: 3.0.914.7250\r\n"
"url: http://www.corp.google.com/~av/45/opt/SearchWithGoogleUpdate.exe\r\n"
"launch-action: custom-action\r\n"
"launch-target: SearchWithGoogleUpdate.exe\r\n"
"signature: c08a3f4438e1442c4fe5678ee147cf6c5516e5d62bb64e\r\n"
"rlz: 1R1_____en__252\r\n"
"rlzXX: 1R1_____en__250\r\n"
"crc32: B12CC79C\r\n"
"rlzT4 1T4_____en__251\r\n"
"rlzT4: 1T4_____en__252\r\n"
"rlz\r\n",
NULL
};
const char* kGoodPingResponses[] = {
"version: 3.0.914.7250\r\n"
"url: http://www.corp.google.com/~av/45/opt/SearchWithGoogleUpdate.exe\r\n"
"launch-action: custom-action\r\n"
"launch-target: SearchWithGoogleUpdate.exe\r\n"
"signature: c08a3f4438e1442c4fe5678ee147cf6c5516e5d62bb64e\r\n"
"rlz: 1R1_____en__252\r\n"
"rlzXX: 1R1_____en__250\r\n"
"rlzT4 1T4_____en__251\r\n"
"rlzT4: 1T4_____en__252\r\n"
"rlz\r\n"
"crc32: D6FD55A3",
"version: 3.0.914.7250\r\n"
"url: http://www.corp.google.com/~av/45/opt/SearchWithGoogleUpdate.exe\r\n"
"launch-action: custom-action\r\n"
"launch-target: SearchWithGoogleUpdate.exe\r\n"
"signature: c08a3f4438e1442c4fe5678ee147cf6c5516e5d62bb64e\r\n"
"rlz: 1R1_____en__252\r\n"
"rlzXX: 1R1_____en__250\r\n"
"rlzT4 1T4_____en__251\r\n"
"rlzT4: 1T4_____en__252\r\n"
"rlz\r\n"
"crc32: D6FD55A3\r\n"
"extradata: not checksummed",
NULL
};
for (int i = 0; kBadPingResponses[i]; i++)
EXPECT_FALSE(rlz_lib::IsPingResponseValid(kBadPingResponses[i], NULL));
for (int i = 0; kGoodPingResponses[i]; i++)
EXPECT_TRUE(rlz_lib::IsPingResponseValid(kGoodPingResponses[i], NULL));
}
TEST_F(RlzLibTest, ParsePingResponse) {
const char* kPingResponse =
"version: 3.0.914.7250\r\n"
"url: http://www.corp.google.com/~av/45/opt/SearchWithGoogleUpdate.exe\r\n"
"launch-action: custom-action\r\n"
"launch-target: SearchWithGoogleUpdate.exe\r\n"
"signature: c08a3f4438e1442c4fe5678ee147cf6c5516e5d62bb64e\r\n"
"rlz: 1R1_____en__252\r\n" // Invalid RLZ - no access point.
"rlzXX: 1R1_____en__250\r\n" // Invalid RLZ - bad access point.
"rlzT4 1T4_____en__251\r\n" // Invalid RLZ - missing colon.
"rlzT4: 1T4_____en__252\r\n" // GoodRLZ.
"events: I7S,W1I\r\n" // Clear all events.
"rlz\r\n"
"dcc: dcc_value\r\n"
"crc32: F9070F81";
#if defined(OS_WIN)
EXPECT_TRUE(rlz_lib::MachineDealCode::Set("dcc_value2"));
#endif
// Record some product events to check that they get cleared.
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
EXPECT_TRUE(rlz_lib::SetAccessPointRlz(
rlz_lib::IETB_SEARCH_BOX, "TbRlzValue"));
EXPECT_TRUE(rlz_lib::ParsePingResponse(rlz_lib::TOOLBAR_NOTIFIER,
kPingResponse));
#if defined(OS_WIN)
EXPECT_TRUE(rlz_lib::MachineDealCode::Set("dcc_value"));
#endif
EXPECT_TRUE(rlz_lib::ParsePingResponse(rlz_lib::TOOLBAR_NOTIFIER,
kPingResponse));
char value[50];
EXPECT_TRUE(rlz_lib::GetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, value, 50));
EXPECT_STREQ("1T4_____en__252", value);
EXPECT_FALSE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
value, 50));
EXPECT_STREQ("", value);
const char* kPingResponse2 =
"rlzT4: 1T4_____de__253 \r\n" // Good with extra spaces.
"crc32: 321334F5\r\n";
EXPECT_TRUE(rlz_lib::ParsePingResponse(rlz_lib::TOOLBAR_NOTIFIER,
kPingResponse2));
EXPECT_TRUE(rlz_lib::GetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, value, 50));
EXPECT_STREQ("1T4_____de__253", value);
const char* kPingResponse3 =
"crc32: 0\r\n"; // Good RLZ - empty response.
EXPECT_TRUE(rlz_lib::ParsePingResponse(rlz_lib::TOOLBAR_NOTIFIER,
kPingResponse3));
EXPECT_STREQ("1T4_____de__253", value);
}
// Test whether a stateful event will only be sent in financial pings once.
TEST_F(RlzLibTest, ParsePingResponseWithStatefulEvents) {
const char* kPingResponse =
"version: 3.0.914.7250\r\n"
"url: http://www.corp.google.com/~av/45/opt/SearchWithGoogleUpdate.exe\r\n"
"launch-action: custom-action\r\n"
"launch-target: SearchWithGoogleUpdate.exe\r\n"
"signature: c08a3f4438e1442c4fe5678ee147cf6c5516e5d62bb64e\r\n"
"rlzT4: 1T4_____en__252\r\n" // GoodRLZ.
"events: I7S,W1I\r\n" // Clear all events.
"stateful-events: W1I\r\n" // W1I as an stateful event.
"rlz\r\n"
"dcc: dcc_value\r\n"
"crc32: 55191759";
EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
// Record some product events to check that they get cleared.
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
EXPECT_TRUE(rlz_lib::SetAccessPointRlz(
rlz_lib::IETB_SEARCH_BOX, "TbRlzValue"));
EXPECT_TRUE(rlz_lib::ParsePingResponse(rlz_lib::TOOLBAR_NOTIFIER,
kPingResponse));
// Check all the events sent earlier are cleared.
char value[50];
EXPECT_FALSE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
value, 50));
EXPECT_STREQ("", value);
// Record both events (one is stateless and the other is stateful) again.
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
// Check the stateful event won't be sent again while the stateless one will.
EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
value, 50));
EXPECT_STREQ("events=I7S", value);
// Test that stateful events are cleared by ClearAllProductEvents(). After
// calling it, trying to record a stateful again should result in it being
// recorded again.
EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
value, 50));
EXPECT_STREQ("events=W1I", value);
}
TEST_F(RlzLibTest, SendFinancialPing) {
// We don't really check a value or result in this test. All this does is
// attempt to ping the financial server, which you can verify in Fiddler.
// TODO: Make this a measurable test.
#if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
#if defined(OS_MACOSX)
base::mac::ScopedNSAutoreleasePool pool;
#endif
base::Thread::Options options;
options.message_loop_type = MessageLoop::TYPE_IO;
base::Thread io_thread("rlz_unittest_io_thread");
ASSERT_TRUE(io_thread.StartWithOptions(options));
scoped_refptr<TestURLRequestContextGetter> context =
new TestURLRequestContextGetter(
io_thread.message_loop()->message_loop_proxy());
rlz_lib::SetURLRequestContext(context.get());
class URLRequestRAII {
public:
URLRequestRAII(net::URLRequestContextGetter* context) {
rlz_lib::SetURLRequestContext(context);
}
~URLRequestRAII() {
rlz_lib::SetURLRequestContext(NULL);
}
};
URLRequestRAII set_context(context.get());
#endif
MachineDealCodeHelper::Clear();
#if defined(OS_WIN)
EXPECT_TRUE(rlz_lib::MachineDealCode::Set("dcc_value"));
#endif
EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX,
"TbRlzValue"));
EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
rlz_lib::AccessPoint points[] =
{rlz_lib::IETB_SEARCH_BOX, rlz_lib::NO_ACCESS_POINT,
rlz_lib::NO_ACCESS_POINT};
std::string request;
rlz_lib::SendFinancialPing(rlz_lib::TOOLBAR_NOTIFIER, points,
"swg", "GGLA", "SwgProductId1234", "en-UK", false,
/*skip_time_check=*/true);
}
TEST_F(RlzLibTest, ClearProductState) {
MachineDealCodeHelper::Clear();
EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX,
"TbRlzValue"));
EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::GD_DESKBAND,
"GdbRlzValue"));
rlz_lib::AccessPoint points[] =
{ rlz_lib::IETB_SEARCH_BOX, rlz_lib::NO_ACCESS_POINT };
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IETB_SEARCH_BOX, rlz_lib::INSTALL));
rlz_lib::AccessPoint points2[] =
{ rlz_lib::IETB_SEARCH_BOX,
rlz_lib::GD_DESKBAND,
rlz_lib::NO_ACCESS_POINT };
char cgi[2048];
EXPECT_TRUE(rlz_lib::GetPingParams(rlz_lib::TOOLBAR_NOTIFIER, points2,
cgi, 2048));
EXPECT_STREQ("rep=2&rlz=T4:TbRlzValue,D1:GdbRlzValue", cgi);
EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
cgi, 2048));
std::string events(cgi);
EXPECT_LT(0u, events.find("I7S"));
EXPECT_LT(0u, events.find("T4I"));
EXPECT_LT(0u, events.find("T4R"));
rlz_lib::ClearProductState(rlz_lib::TOOLBAR_NOTIFIER, points);
EXPECT_TRUE(rlz_lib::GetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX,
cgi, 2048));
EXPECT_STREQ("", cgi);
EXPECT_TRUE(rlz_lib::GetAccessPointRlz(rlz_lib::GD_DESKBAND,
cgi, 2048));
EXPECT_STREQ("GdbRlzValue", cgi);
EXPECT_FALSE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
cgi, 2048));
EXPECT_STREQ("", cgi);
}
#if defined(OS_WIN)
template<class T>
class typed_buffer_ptr {
scoped_array<char> buffer_;
public:
typed_buffer_ptr() {
}
explicit typed_buffer_ptr(size_t size) : buffer_(new char[size]) {
}
void reset(size_t size) {
buffer_.reset(new char[size]);
}
operator T*() {
return reinterpret_cast<T*>(buffer_.get());
}
};
namespace rlz_lib {
bool HasAccess(PSID sid, ACCESS_MASK access_mask, ACL* dacl);
}
bool EmptyAcl(ACL* acl) {
ACL_SIZE_INFORMATION info;
bool ret = GetAclInformation(acl, &info, sizeof(info), AclSizeInformation);
EXPECT_TRUE(ret);
for (DWORD i = 0; i < info.AceCount && ret; ++i) {
ret = DeleteAce(acl, 0);
EXPECT_TRUE(ret);
}
return ret;
}
TEST_F(RlzLibTest, HasAccess) {
// Create a SID that represents ALL USERS.
DWORD users_sid_size = SECURITY_MAX_SID_SIZE;
typed_buffer_ptr<SID> users_sid(users_sid_size);
CreateWellKnownSid(WinBuiltinUsersSid, NULL, users_sid, &users_sid_size);
// RLZ always asks for KEY_ALL_ACCESS access to the key. This is what we
// test here.
// No ACL mean no access.
EXPECT_FALSE(rlz_lib::HasAccess(users_sid, KEY_ALL_ACCESS, NULL));
// Create an ACL for these tests.
const DWORD kMaxAclSize = 1024;
typed_buffer_ptr<ACL> dacl(kMaxAclSize);
InitializeAcl(dacl, kMaxAclSize, ACL_REVISION);
// Empty DACL mean no access.
EXPECT_FALSE(rlz_lib::HasAccess(users_sid, KEY_ALL_ACCESS, dacl));
// ACE without all needed privileges should mean no access.
EXPECT_TRUE(AddAccessAllowedAce(dacl, ACL_REVISION, KEY_READ, users_sid));
EXPECT_FALSE(rlz_lib::HasAccess(users_sid, KEY_ALL_ACCESS, dacl));
// ACE without all needed privileges should mean no access.
EXPECT_TRUE(EmptyAcl(dacl));
EXPECT_TRUE(AddAccessAllowedAce(dacl, ACL_REVISION, KEY_WRITE, users_sid));
EXPECT_FALSE(rlz_lib::HasAccess(users_sid, KEY_ALL_ACCESS, dacl));
// A deny ACE before an allow ACE should not give access.
EXPECT_TRUE(EmptyAcl(dacl));
EXPECT_TRUE(AddAccessDeniedAce(dacl, ACL_REVISION, KEY_ALL_ACCESS,
users_sid));
EXPECT_TRUE(AddAccessAllowedAce(dacl, ACL_REVISION, KEY_ALL_ACCESS,
users_sid));
EXPECT_FALSE(rlz_lib::HasAccess(users_sid, KEY_ALL_ACCESS, dacl));
// A deny ACE before an allow ACE should not give access.
EXPECT_TRUE(EmptyAcl(dacl));
EXPECT_TRUE(AddAccessDeniedAce(dacl, ACL_REVISION, KEY_READ, users_sid));
EXPECT_TRUE(AddAccessAllowedAce(dacl, ACL_REVISION, KEY_ALL_ACCESS,
users_sid));
EXPECT_FALSE(rlz_lib::HasAccess(users_sid, KEY_ALL_ACCESS, dacl));
// An allow ACE without all required bits should not give access.
EXPECT_TRUE(EmptyAcl(dacl));
EXPECT_TRUE(AddAccessAllowedAce(dacl, ACL_REVISION, KEY_WRITE, users_sid));
EXPECT_FALSE(rlz_lib::HasAccess(users_sid, KEY_ALL_ACCESS, dacl));
// An allow ACE with all required bits should give access.
EXPECT_TRUE(EmptyAcl(dacl));
EXPECT_TRUE(AddAccessAllowedAce(dacl, ACL_REVISION, KEY_ALL_ACCESS,
users_sid));
EXPECT_TRUE(rlz_lib::HasAccess(users_sid, KEY_ALL_ACCESS, dacl));
// A deny ACE after an allow ACE should not give access.
EXPECT_TRUE(EmptyAcl(dacl));
EXPECT_TRUE(AddAccessAllowedAce(dacl, ACL_REVISION, KEY_ALL_ACCESS,
users_sid));
EXPECT_TRUE(AddAccessDeniedAce(dacl, ACL_REVISION, KEY_READ, users_sid));
EXPECT_TRUE(rlz_lib::HasAccess(users_sid, KEY_ALL_ACCESS, dacl));
// An inherit-only allow ACE should not give access.
EXPECT_TRUE(EmptyAcl(dacl));
EXPECT_TRUE(AddAccessAllowedAceEx(dacl, ACL_REVISION, INHERIT_ONLY_ACE,
KEY_ALL_ACCESS, users_sid));
EXPECT_FALSE(rlz_lib::HasAccess(users_sid, KEY_ALL_ACCESS, dacl));
// An inherit-only deny ACE should not apply.
EXPECT_TRUE(EmptyAcl(dacl));
EXPECT_TRUE(AddAccessDeniedAceEx(dacl, ACL_REVISION, INHERIT_ONLY_ACE,
KEY_ALL_ACCESS, users_sid));
EXPECT_TRUE(AddAccessAllowedAce(dacl, ACL_REVISION, KEY_ALL_ACCESS,
users_sid));
EXPECT_TRUE(rlz_lib::HasAccess(users_sid, KEY_ALL_ACCESS, dacl));
}
#endif
TEST_F(RlzLibTest, BrandingRecordProductEvent) {
// Don't run these tests if a supplementary brand is already in place. That
// way we can control the branding.
if (!rlz_lib::SupplementaryBranding::GetBrand().empty())
return;
char cgi_50[50];
// Record different events for the same product with diffrent branding, and
// make sure that the information remains separate.
EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
{
rlz_lib::SupplementaryBranding branding("TEST");
EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
}
// Test that recording events with the default brand and a supplementary
// brand don't overwrite each other.
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
cgi_50, 50));
EXPECT_STREQ("events=I7S", cgi_50);
{
rlz_lib::SupplementaryBranding branding("TEST");
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::INSTALL));
EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
cgi_50, 50));
EXPECT_STREQ("events=I7I", cgi_50);
}
EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
cgi_50, 50));
EXPECT_STREQ("events=I7S", cgi_50);
}
TEST_F(RlzLibTest, BrandingSetAccessPointRlz) {
// Don't run these tests if a supplementary brand is already in place. That
// way we can control the branding.
if (!rlz_lib::SupplementaryBranding::GetBrand().empty())
return;
char rlz_50[50];
// Test that setting RLZ strings with the default brand and a supplementary
// brand don't overwrite each other.
EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, "IeTbRlz"));
EXPECT_TRUE(rlz_lib::GetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, rlz_50, 50));
EXPECT_STREQ("IeTbRlz", rlz_50);
{
rlz_lib::SupplementaryBranding branding("TEST");
EXPECT_TRUE(rlz_lib::SetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, "SuppRlz"));
EXPECT_TRUE(rlz_lib::GetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, rlz_50,
50));
EXPECT_STREQ("SuppRlz", rlz_50);
}
EXPECT_TRUE(rlz_lib::GetAccessPointRlz(rlz_lib::IETB_SEARCH_BOX, rlz_50, 50));
EXPECT_STREQ("IeTbRlz", rlz_50);
}
TEST_F(RlzLibTest, BrandingWithStatefulEvents) {
// Don't run these tests if a supplementary brand is already in place. That
// way we can control the branding.
if (!rlz_lib::SupplementaryBranding::GetBrand().empty())
return;
const char* kPingResponse =
"version: 3.0.914.7250\r\n"
"url: http://www.corp.google.com/~av/45/opt/SearchWithGoogleUpdate.exe\r\n"
"launch-action: custom-action\r\n"
"launch-target: SearchWithGoogleUpdate.exe\r\n"
"signature: c08a3f4438e1442c4fe5678ee147cf6c5516e5d62bb64e\r\n"
"rlzT4: 1T4_____en__252\r\n" // GoodRLZ.
"events: I7S,W1I\r\n" // Clear all events.
"stateful-events: W1I\r\n" // W1I as an stateful event.
"rlz\r\n"
"dcc: dcc_value\r\n"
"crc32: 55191759";
EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
{
rlz_lib::SupplementaryBranding branding("TEST");
EXPECT_TRUE(rlz_lib::ClearAllProductEvents(rlz_lib::TOOLBAR_NOTIFIER));
}
// Record some product events for the default and supplementary brand.
// Check that they get cleared only for the default brand.
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
{
rlz_lib::SupplementaryBranding branding("TEST");
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
}
EXPECT_TRUE(rlz_lib::ParsePingResponse(rlz_lib::TOOLBAR_NOTIFIER,
kPingResponse));
// Check all the events sent earlier are cleared only for default brand.
char value[50];
EXPECT_FALSE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
value, 50));
EXPECT_STREQ("", value);
{
rlz_lib::SupplementaryBranding branding("TEST");
EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
value, 50));
EXPECT_STREQ("events=I7S,W1I", value);
}
// Record both events (one is stateless and the other is stateful) again.
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
EXPECT_TRUE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_HOME_PAGE, rlz_lib::INSTALL));
// Check the stateful event won't be sent again while the stateless one will.
EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
value, 50));
EXPECT_STREQ("events=I7S", value);
{
rlz_lib::SupplementaryBranding branding("TEST");
EXPECT_TRUE(rlz_lib::ParsePingResponse(rlz_lib::TOOLBAR_NOTIFIER,
kPingResponse));
EXPECT_FALSE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
value, 50));
EXPECT_STREQ("", value);
}
EXPECT_TRUE(rlz_lib::GetProductEventsAsCgi(rlz_lib::TOOLBAR_NOTIFIER,
value, 50));
EXPECT_STREQ("events=I7S", value);
}
#if defined(OS_MACOSX)
class ReadonlyRlzDirectoryTest : public RlzLibTestNoMachineState {
protected:
virtual void SetUp() OVERRIDE;
};
void ReadonlyRlzDirectoryTest::SetUp() {
RlzLibTestNoMachineState::SetUp();
// Make the rlz directory non-writeable.
chmod(temp_dir_.path().value().c_str(), 0500);
}
TEST_F(ReadonlyRlzDirectoryTest, WriteFails) {
// The rlz test runner runs every test twice: Once normally, and once with
// a SupplementaryBranding on the stack. In the latter case, the rlz lock
// has already been acquired before the rlz directory got changed to
// read-only, which makes this test pointless. So run it only in the first
// pass.
if (!rlz_lib::SupplementaryBranding::GetBrand().empty())
return;
EXPECT_FALSE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::SET_TO_GOOGLE));
}
// Regression test for http://crbug.com/121255
TEST_F(ReadonlyRlzDirectoryTest, SupplementaryBrandingDoesNotCrash) {
// See the comment at the top of WriteFails.
if (!rlz_lib::SupplementaryBranding::GetBrand().empty())
return;
rlz_lib::SupplementaryBranding branding("TEST");
EXPECT_FALSE(rlz_lib::RecordProductEvent(rlz_lib::TOOLBAR_NOTIFIER,
rlz_lib::IE_DEFAULT_SEARCH, rlz_lib::INSTALL));
}
#endif

116
rlz/lib/rlz_value_store.h Normal file

@ -0,0 +1,116 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef RLZ_VALUE_STORE_H_
#define RLZ_VALUE_STORE_H_
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "rlz/lib/rlz_enums.h"
#if defined(OS_WIN)
#include "rlz/win/lib/lib_mutex.h"
#endif
#if defined(OS_MACOSX)
#include "base/mac/scoped_nsautorelease_pool.h"
#endif
#include <string>
#include <vector>
class FilePath;
namespace rlz_lib {
// Abstracts away rlz's key value store. On windows, this usually writes to
// the registry. On mac, it writes to an NSDefaults object.
class RlzValueStore {
public:
virtual ~RlzValueStore() {}
enum AccessType { kReadAccess, kWriteAccess };
virtual bool HasAccess(AccessType type) = 0;
// Ping times.
virtual bool WritePingTime(Product product, int64 time) = 0;
virtual bool ReadPingTime(Product product, int64* time) = 0;
virtual bool ClearPingTime(Product product) = 0;
// Access point RLZs.
virtual bool WriteAccessPointRlz(AccessPoint access_point,
const char* new_rlz) = 0;
virtual bool ReadAccessPointRlz(AccessPoint access_point,
char* rlz, // At most kMaxRlzLength + 1 bytes
size_t rlz_size) = 0;
virtual bool ClearAccessPointRlz(AccessPoint access_point) = 0;
// Product events.
// Stores |event_rlz| for product |product| as product event.
virtual bool AddProductEvent(Product product, const char* event_rlz) = 0;
// Appends all events for |product| to |events|, in arbirtrary order.
virtual bool ReadProductEvents(Product product,
std::vector<std::string>* events) = 0;
// Removes the stored event |event_rlz| for |product| if it exists.
virtual bool ClearProductEvent(Product product, const char* event_rlz) = 0;
// Removes all stored product events for |product|.
virtual bool ClearAllProductEvents(Product product) = 0;
// Stateful events.
// Stores |event_rlz| for product |product| as stateful event.
virtual bool AddStatefulEvent(Product product, const char* event_rlz) = 0;
// Checks if |event_rlz| has been stored as stateful event for |product|.
virtual bool IsStatefulEvent(Product product, const char* event_rlz) = 0;
// Removes all stored stateful events for |product|.
virtual bool ClearAllStatefulEvents(Product product) = 0;
// Tells the value store to clean up unimportant internal data structures, for
// example empty registry folders, that might remain after clearing other
// data. Best-effort.
virtual void CollectGarbage() = 0;
};
// All methods of RlzValueStore must stays consistent even when accessed from
// multiple threads in multiple processes. To enforce this through the type
// system, the only way to access the RlzValueStore is through a
// ScopedRlzValueStoreLock, which is a cross-process lock. It is active while
// it is in scope. If the class fails to acquire a lock, its GetStore() method
// returns NULL. If the lock fails to be acquired, it must not be taken
// recursively. That is, all user code should look like this:
// ScopedRlzValueStoreLock lock;
// RlzValueStore* store = lock.GetStore();
// if (!store)
// return some_error_code;
// ...
class ScopedRlzValueStoreLock {
public:
ScopedRlzValueStoreLock();
~ScopedRlzValueStoreLock();
// Returns a RlzValueStore protected by a cross-process lock, or NULL if the
// lock can't be obtained. The lifetime of the returned object is limited to
// the lifetime of this ScopedRlzValueStoreLock object.
RlzValueStore* GetStore();
private:
scoped_ptr<RlzValueStore> store_;
#if defined(OS_WIN)
LibMutex lock_;
#else
base::mac::ScopedNSAutoreleasePool autorelease_pool_;
#endif
};
#if defined(OS_MACOSX)
namespace testing {
// Prefix |directory| to the path where the RLZ data file lives, for tests.
void SetRlzStoreDirectory(const FilePath& directory);
} // namespace testing
#endif // defined(OS_MACOSX)
} // namespace rlz_lib
#endif // RLZ_VALUE_STORE_H_

92
rlz/lib/string_utils.cc Normal file

@ -0,0 +1,92 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// String manipulation functions used in the RLZ library.
#include "rlz/lib/string_utils.h"
#include "rlz/lib/assert.h"
namespace rlz_lib {
bool IsAscii(char letter) {
return (letter >= 0x0 && letter < 0x80);
}
bool GetHexValue(char letter, int* value) {
if (!value) {
ASSERT_STRING("GetHexValue: Invalid output paramter");
return false;
}
*value = 0;
if (letter >= '0' && letter <= '9')
*value = letter - '0';
else if (letter >= 'a' && letter <= 'f')
*value = (letter - 'a') + 0xA;
else if (letter >= 'A' && letter <= 'F')
*value = (letter - 'A') + 0xA;
else
return false;
return true;
}
int HexStringToInteger(const char* text) {
if (!text) {
ASSERT_STRING("HexStringToInteger: text is NULL.");
return 0;
}
int idx = 0;
// Ignore leading whitespace.
while (text[idx] == '\t' || text[idx] == ' ')
idx++;
if ((text[idx] == '0') &&
(text[idx + 1] == 'X' || text[idx + 1] == 'x'))
idx +=2; // String is of the form 0x...
int number = 0;
int digit = 0;
for (; text[idx] != '\0'; idx++) {
if (!GetHexValue(text[idx], &digit)) {
// Ignore trailing whitespaces, but assert on other trailing characters.
bool only_whitespaces = true;
while (only_whitespaces && text[idx])
only_whitespaces = (text[idx++] == ' ');
if (!only_whitespaces)
ASSERT_STRING("HexStringToInteger: text contains non-hex characters.");
return number;
}
number = (number << 4) | digit;
}
return number;
}
bool BytesToString(const unsigned char* data,
int data_len,
std::string* string) {
if (!string)
return false;
string->clear();
if (data_len < 1 || !data)
return false;
static const char kHex[] = "0123456789ABCDEF";
// Fix the buffer size to begin with to avoid repeated re-allocation.
string->resize(data_len * 2);
int index = data_len;
while (index--) {
string->at(2 * index) = kHex[data[index] >> 4]; // high digit
string->at(2 * index + 1) = kHex[data[index] & 0x0F]; // low digit
}
return true;
}
} // namespace rlz_lib

26
rlz/lib/string_utils.h Normal file

@ -0,0 +1,26 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// String manipulation functions used in the RLZ library.
#ifndef RLZ_LIB_STRING_UTILS_H_
#define RLZ_LIB_STRING_UTILS_H_
#include <string>
namespace rlz_lib {
bool IsAscii(char letter);
bool BytesToString(const unsigned char* data,
int data_len,
std::string* string);
bool GetHexValue(char letter, int* value);
int HexStringToInteger(const char* text);
}; // namespace
#endif // RLZ_LIB_STRING_UTILS_H_

@ -0,0 +1,69 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Unit test for string manipulation functions used in the RLZ library.
#include "rlz/lib/string_utils.h"
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/utf_string_conversions.h"
#include "rlz/lib/assert.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
TEST(StringUtilsUnittest, IsAscii) {
rlz_lib::SetExpectedAssertion("");
char bad_letters[] = {'\x80', '\xA0', '\xFF'};
for (size_t i = 0; i < arraysize(bad_letters); ++i)
EXPECT_FALSE(rlz_lib::IsAscii(bad_letters[i]));
char good_letters[] = {'A', '~', '\n', 0x7F, 0x00};
for (size_t i = 0; i < arraysize(good_letters); ++i)
EXPECT_TRUE(rlz_lib::IsAscii(good_letters[i]));
}
TEST(StringUtilsUnittest, HexStringToInteger) {
rlz_lib::SetExpectedAssertion("HexStringToInteger: text is NULL.");
EXPECT_EQ(0, rlz_lib::HexStringToInteger(NULL));
rlz_lib::SetExpectedAssertion("");
EXPECT_EQ(0, rlz_lib::HexStringToInteger(""));
EXPECT_EQ(0, rlz_lib::HexStringToInteger(" "));
EXPECT_EQ(0, rlz_lib::HexStringToInteger(" 0x "));
EXPECT_EQ(0, rlz_lib::HexStringToInteger(" 0x0 "));
EXPECT_EQ(0x12345, rlz_lib::HexStringToInteger("12345"));
EXPECT_EQ(0xa34Ed0, rlz_lib::HexStringToInteger("a34Ed0"));
EXPECT_EQ(0xa34Ed0, rlz_lib::HexStringToInteger("0xa34Ed0"));
EXPECT_EQ(0xa34Ed0, rlz_lib::HexStringToInteger(" 0xa34Ed0"));
EXPECT_EQ(0xa34Ed0, rlz_lib::HexStringToInteger("0xa34Ed0 "));
EXPECT_EQ(0xa34Ed0, rlz_lib::HexStringToInteger(" 0xa34Ed0 "));
EXPECT_EQ(0xa34Ed0, rlz_lib::HexStringToInteger(" 0x000a34Ed0 "));
EXPECT_EQ(0xa34Ed0, rlz_lib::HexStringToInteger(" 000a34Ed0 "));
rlz_lib::SetExpectedAssertion(
"HexStringToInteger: text contains non-hex characters.");
EXPECT_EQ(0x12ff, rlz_lib::HexStringToInteger("12ffg"));
EXPECT_EQ(0x12f, rlz_lib::HexStringToInteger("12f 121"));
EXPECT_EQ(0x12f, rlz_lib::HexStringToInteger("12f 121"));
EXPECT_EQ(0, rlz_lib::HexStringToInteger("g12f"));
EXPECT_EQ(0, rlz_lib::HexStringToInteger(" 0x0 \n"));
rlz_lib::SetExpectedAssertion("");
}
TEST(StringUtilsUnittest, TestBytesToString) {
unsigned char data[] = {0x1E, 0x00, 0x21, 0x67, 0xFF};
std::string result;
EXPECT_FALSE(rlz_lib::BytesToString(NULL, 5, &result));
EXPECT_FALSE(rlz_lib::BytesToString(data, 5, NULL));
EXPECT_FALSE(rlz_lib::BytesToString(NULL, 5, NULL));
EXPECT_TRUE(rlz_lib::BytesToString(data, 5, &result));
EXPECT_EQ(std::string("1E002167FF"), result);
EXPECT_TRUE(rlz_lib::BytesToString(data, 4, &result));
EXPECT_EQ(std::string("1E002167"), result);
}

@ -0,0 +1,147 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/network/IOEthernetInterface.h>
#include <IOKit/network/IONetworkInterface.h>
#include <IOKit/network/IOEthernetController.h>
#include "base/logging.h"
#include "base/mac/foundation_util.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/mac/scoped_ioobject.h"
#include "base/string16.h"
#include "base/stringprintf.h"
#include "base/sys_string_conversions.h"
#include "base/utf_string_conversions.h"
namespace rlz_lib {
namespace {
// See http://developer.apple.com/library/mac/#technotes/tn1103/_index.html
// The caller is responsible for freeing |matching_services|.
bool FindEthernetInterfaces(io_iterator_t* matching_services) {
base::mac::ScopedCFTypeRef<CFMutableDictionaryRef> matching_dict(
IOServiceMatching(kIOEthernetInterfaceClass));
if (!matching_dict)
return false;
base::mac::ScopedCFTypeRef<CFMutableDictionaryRef> primary_interface(
CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks));
if (!primary_interface)
return false;
CFDictionarySetValue(
primary_interface, CFSTR(kIOPrimaryInterface), kCFBooleanTrue);
CFDictionarySetValue(
matching_dict, CFSTR(kIOPropertyMatchKey), primary_interface);
kern_return_t kern_result = IOServiceGetMatchingServices(
kIOMasterPortDefault, matching_dict.release(), matching_services);
return kern_result == KERN_SUCCESS;
}
bool GetMACAddressFromIterator(io_iterator_t primary_interface_iterator,
uint8_t* buffer, size_t buffer_size) {
if (buffer_size < kIOEthernetAddressSize)
return false;
bool success = false;
bzero(buffer, buffer_size);
base::mac::ScopedIOObject<io_object_t> primary_interface;
while (primary_interface.reset(IOIteratorNext(primary_interface_iterator)),
primary_interface) {
io_object_t primary_interface_parent;
kern_return_t kern_result = IORegistryEntryGetParentEntry(
primary_interface, kIOServicePlane, &primary_interface_parent);
base::mac::ScopedIOObject<io_object_t> primary_interface_parent_deleter(
primary_interface_parent);
success = kern_result == KERN_SUCCESS;
if (!success)
continue;
base::mac::ScopedCFTypeRef<CFTypeRef> mac_data(
IORegistryEntryCreateCFProperty(primary_interface_parent,
CFSTR(kIOMACAddress),
kCFAllocatorDefault,
0));
CFDataRef mac_data_data = base::mac::CFCast<CFDataRef>(mac_data);
if (mac_data_data) {
CFDataGetBytes(
mac_data_data, CFRangeMake(0, kIOEthernetAddressSize), buffer);
}
}
return success;
}
bool GetMacAddress(unsigned char* buffer, size_t size) {
io_iterator_t primary_interface_iterator;
if (!FindEthernetInterfaces(&primary_interface_iterator))
return false;
bool result = GetMACAddressFromIterator(
primary_interface_iterator, buffer, size);
IOObjectRelease(primary_interface_iterator);
return result;
}
CFStringRef CopySerialNumber() {
base::mac::ScopedIOObject<io_service_t> expert_device(
IOServiceGetMatchingService(kIOMasterPortDefault,
IOServiceMatching("IOPlatformExpertDevice")));
if (!expert_device)
return NULL;
base::mac::ScopedCFTypeRef<CFTypeRef> serial_number(
IORegistryEntryCreateCFProperty(expert_device,
CFSTR(kIOPlatformSerialNumberKey),
kCFAllocatorDefault,
0));
CFStringRef serial_number_cfstring =
base::mac::CFCast<CFStringRef>(serial_number);
if (!serial_number_cfstring)
return NULL;
ignore_result(serial_number.release());
return serial_number_cfstring;
}
} // namespace
bool GetRawMachineId(string16* data, int* more_data) {
uint8_t mac_address[kIOEthernetAddressSize];
data->clear();
if (GetMacAddress(mac_address, sizeof(mac_address))) {
*data += ASCIIToUTF16(StringPrintf("mac:%02x%02x%02x%02x%02x%02x",
mac_address[0], mac_address[1], mac_address[2],
mac_address[3], mac_address[4], mac_address[5]));
}
// A MAC address is enough to uniquely identify a machine, but it's only 6
// bytes, 3 of which are manufacturer-determined. To make brute-forcing the
// SHA1 of this harder, also append the system's serial number.
CFStringRef serial = CopySerialNumber();
if (serial) {
if (!data->empty())
*data += UTF8ToUTF16(" ");
*data += UTF8ToUTF16("serial:") + base::SysCFStringRefToUTF16(serial);
CFRelease(serial);
}
// On windows, this is set to the volume id. Since it's not scrambled before
// being sent, just set it to 1.
*more_data = 1;
return true;
}
} // namespace rlz_lib

@ -0,0 +1,80 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef RLZ_MAC_LIB_RLZ_VALUE_STORE_MAC_H_
#define RLZ_MAC_LIB_RLZ_VALUE_STORE_MAC_H_
#include "rlz/lib/rlz_value_store.h"
#include "base/compiler_specific.h"
#include "base/memory/scoped_nsobject.h"
@class NSDictionary;
@class NSMutableDictionary;
namespace rlz_lib {
// An implementation of RlzValueStore for mac. It stores information in a
// plist file in the user's Application Support folder.
class RlzValueStoreMac : public RlzValueStore {
public:
virtual bool HasAccess(AccessType type) OVERRIDE;
virtual bool WritePingTime(Product product, int64 time) OVERRIDE;
virtual bool ReadPingTime(Product product, int64* time) OVERRIDE;
virtual bool ClearPingTime(Product product) OVERRIDE;
virtual bool WriteAccessPointRlz(AccessPoint access_point,
const char* new_rlz) OVERRIDE;
virtual bool ReadAccessPointRlz(AccessPoint access_point,
char* rlz,
size_t rlz_size) OVERRIDE;
virtual bool ClearAccessPointRlz(AccessPoint access_point) OVERRIDE;
virtual bool AddProductEvent(Product product, const char* event_rlz) OVERRIDE;
virtual bool ReadProductEvents(Product product,
std::vector<std::string>* events) OVERRIDE;
virtual bool ClearProductEvent(Product product,
const char* event_rlz) OVERRIDE;
virtual bool ClearAllProductEvents(Product product) OVERRIDE;
virtual bool AddStatefulEvent(Product product,
const char* event_rlz) OVERRIDE;
virtual bool IsStatefulEvent(Product product,
const char* event_rlz) OVERRIDE;
virtual bool ClearAllStatefulEvents(Product product) OVERRIDE;
virtual void CollectGarbage() OVERRIDE;
private:
// |dict| is the dictionary that backs all data. plist_path is the name of the
// plist file, used solely for implementing HasAccess().
RlzValueStoreMac(NSMutableDictionary* dict, NSString* plist_path);
virtual ~RlzValueStoreMac();
friend class ScopedRlzValueStoreLock;
// Returns the backing dictionary that should be written to disk.
NSDictionary* dictionary();
// Returns the dictionary to which all data should be written. Usually, this
// is just |dictionary()|, but if supplementary branding is used, it's a
// subdirectory at key "brand_<supplementary branding code>".
// Note that windows stores data at
// rlz/name (e.g. "pingtime")/supplementalbranding/productcode
// Mac on the other hand does
// supplementalbranding/productcode/pingtime.
NSMutableDictionary* WorkingDict();
// Returns the subdirectory of |WorkingDict()| used to store data for
// product p.
NSMutableDictionary* ProductDict(Product p);
scoped_nsobject<NSMutableDictionary> dict_;
scoped_nsobject<NSString> plist_path_;
DISALLOW_COPY_AND_ASSIGN(RlzValueStoreMac);
};
} // namespace rlz_lib
#endif // RLZ_MAC_LIB_RLZ_VALUE_STORE_MAC_H_

@ -0,0 +1,447 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "rlz/mac/lib/rlz_value_store_mac.h"
#include "base/mac/foundation_util.h"
#include "base/file_path.h"
#include "base/logging.h"
#include "base/sys_string_conversions.h"
#include "rlz/lib/assert.h"
#include "rlz/lib/lib_values.h"
#include "rlz/lib/rlz_lib.h"
#import <Foundation/Foundation.h>
#include <pthread.h>
using base::mac::ObjCCast;
namespace rlz_lib {
// These are written to disk and should not be changed.
NSString* const kPingTimeKey = @"pingTime";
NSString* const kAccessPointKey = @"accessPoints";
NSString* const kProductEventKey = @"productEvents";
NSString* const kStatefulEventKey = @"statefulEvents";
namespace {
NSString* GetNSProductName(Product product) {
return base::SysUTF8ToNSString(GetProductName(product));
}
NSString* GetNSAccessPointName(AccessPoint p) {
return base::SysUTF8ToNSString(GetAccessPointName(p));
}
// Retrieves a subdictionary in |p| for key |k|, creating it if necessary.
// If the dictionary contains an object for |k| that is not a mutable
// dictionary, that object is replaced with an empty mutable dictinary.
NSMutableDictionary* GetOrCreateDict(
NSMutableDictionary* p, NSString* k) {
NSMutableDictionary* d = ObjCCast<NSMutableDictionary>([p objectForKey:k]);
if (!d) {
d = [NSMutableDictionary dictionaryWithCapacity:0];
[p setObject:d forKey:k];
}
return d;
}
} // namespace
RlzValueStoreMac::RlzValueStoreMac(NSMutableDictionary* dict,
NSString* plist_path)
: dict_([dict retain]), plist_path_([plist_path retain]) {
}
RlzValueStoreMac::~RlzValueStoreMac() {
}
bool RlzValueStoreMac::HasAccess(AccessType type) {
NSFileManager* manager = [NSFileManager defaultManager];
switch (type) {
case kReadAccess: return [manager isReadableFileAtPath:plist_path_];
case kWriteAccess: return [manager isWritableFileAtPath:plist_path_];
}
}
bool RlzValueStoreMac::WritePingTime(Product product, int64 time) {
NSNumber* n = [NSNumber numberWithLongLong:time];
[ProductDict(product) setObject:n forKey:kPingTimeKey];
return true;
}
bool RlzValueStoreMac::ReadPingTime(Product product, int64* time) {
if (NSNumber* n =
ObjCCast<NSNumber>([ProductDict(product) objectForKey:kPingTimeKey])) {
*time = [n longLongValue];
return true;
}
return false;
}
bool RlzValueStoreMac::ClearPingTime(Product product) {
[ProductDict(product) removeObjectForKey:kPingTimeKey];
return true;
}
bool RlzValueStoreMac::WriteAccessPointRlz(AccessPoint access_point,
const char* new_rlz) {
NSMutableDictionary* d = GetOrCreateDict(WorkingDict(), kAccessPointKey);
[d setObject:base::SysUTF8ToNSString(new_rlz)
forKey:GetNSAccessPointName(access_point)];
return true;
}
bool RlzValueStoreMac::ReadAccessPointRlz(AccessPoint access_point,
char* rlz,
size_t rlz_size) {
// Reading a non-existent access point counts as success.
if (NSDictionary* d = ObjCCast<NSDictionary>(
[WorkingDict() objectForKey:kAccessPointKey])) {
NSString* val = ObjCCast<NSString>(
[d objectForKey:GetNSAccessPointName(access_point)]);
if (!val) {
if (rlz_size > 0)
rlz[0] = '\0';
return true;
}
std::string s = base::SysNSStringToUTF8(val);
if (s.size() >= rlz_size) {
rlz[0] = 0;
ASSERT_STRING("GetAccessPointRlz: Insufficient buffer size");
return false;
}
strncpy(rlz, s.c_str(), rlz_size);
return true;
}
if (rlz_size > 0)
rlz[0] = '\0';
return true;
}
bool RlzValueStoreMac::ClearAccessPointRlz(AccessPoint access_point) {
if (NSMutableDictionary* d = ObjCCast<NSMutableDictionary>(
[WorkingDict() objectForKey:kAccessPointKey])) {
[d removeObjectForKey:GetNSAccessPointName(access_point)];
}
return true;
}
bool RlzValueStoreMac::AddProductEvent(Product product,
const char* event_rlz) {
[GetOrCreateDict(ProductDict(product), kProductEventKey)
setObject:[NSNumber numberWithBool:YES]
forKey:base::SysUTF8ToNSString(event_rlz)];
return true;
}
bool RlzValueStoreMac::ReadProductEvents(Product product,
std::vector<std::string>* events) {
if (NSDictionary* d = ObjCCast<NSDictionary>(
[ProductDict(product) objectForKey:kProductEventKey])) {
for (NSString* s in d)
events->push_back(base::SysNSStringToUTF8(s));
return true;
}
return true;
}
bool RlzValueStoreMac::ClearProductEvent(Product product,
const char* event_rlz) {
if (NSMutableDictionary* d = ObjCCast<NSMutableDictionary>(
[ProductDict(product) objectForKey:kProductEventKey])) {
[d removeObjectForKey:base::SysUTF8ToNSString(event_rlz)];
return true;
}
return false;
}
bool RlzValueStoreMac::ClearAllProductEvents(Product product) {
[ProductDict(product) removeObjectForKey:kProductEventKey];
return true;
}
bool RlzValueStoreMac::AddStatefulEvent(Product product,
const char* event_rlz) {
[GetOrCreateDict(ProductDict(product), kStatefulEventKey)
setObject:[NSNumber numberWithBool:YES]
forKey:base::SysUTF8ToNSString(event_rlz)];
return true;
}
bool RlzValueStoreMac::IsStatefulEvent(Product product,
const char* event_rlz) {
if (NSDictionary* d = ObjCCast<NSDictionary>(
[ProductDict(product) objectForKey:kStatefulEventKey])) {
return [d objectForKey:base::SysUTF8ToNSString(event_rlz)] != nil;
}
return false;
}
bool RlzValueStoreMac::ClearAllStatefulEvents(Product product) {
[ProductDict(product) removeObjectForKey:kStatefulEventKey];
return true;
}
void RlzValueStoreMac::CollectGarbage() {
NOTIMPLEMENTED();
}
NSDictionary* RlzValueStoreMac::dictionary() {
return dict_.get();
}
NSMutableDictionary* RlzValueStoreMac::WorkingDict() {
std::string brand(SupplementaryBranding::GetBrand());
if (brand.empty())
return dict_;
NSString* brand_ns =
[@"brand_" stringByAppendingString:base::SysUTF8ToNSString(brand)];
return GetOrCreateDict(dict_.get(), brand_ns);
}
NSMutableDictionary* RlzValueStoreMac::ProductDict(Product p) {
return GetOrCreateDict(WorkingDict(), GetNSProductName(p));
}
namespace {
// Creating a recursive cross-process mutex on windows is one line. On mac,
// there's no primitve for that, so this lock is emulated by an in-process
// mutex to get the recursive part, followed by a cross-process lock for the
// cross-process part.
// This is a struct so that it doesn't need a static initializer.
struct RecursiveCrossProcessLock {
// Tries to acquire a recursive cross-process lock. Note that this _always_
// acquires the in-process lock (if it wasn't already acquired). The parent
// directory of |lock_file| must exist.
bool TryGetCrossProcessLock(NSString* lock_filename);
// Releases the lock. Should always be called, even if
// TryGetCrossProcessLock() returns false.
void ReleaseLock();
pthread_mutex_t recursive_lock_;
pthread_t locking_thread_;
NSDistributedLock* file_lock_;
} g_recursive_lock = {
// PTHREAD_RECURSIVE_MUTEX_INITIALIZER doesn't exist before 10.7 and is buggy
// on 10.7 (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51906#c34), so emulate
// recursive locking with a normal non-recursive mutex.
PTHREAD_MUTEX_INITIALIZER
};
bool RecursiveCrossProcessLock::TryGetCrossProcessLock(
NSString* lock_filename) {
bool just_got_lock = false;
// Emulate a recursive mutex with a non-recursive one.
if (pthread_mutex_trylock(&recursive_lock_) == EBUSY) {
if (pthread_equal(pthread_self(), locking_thread_) == 0) {
// Some other thread has the lock, wait for it.
pthread_mutex_lock(&recursive_lock_);
CHECK(locking_thread_ == 0);
just_got_lock = true;
}
} else {
just_got_lock = true;
}
locking_thread_ = pthread_self();
// Try to acquire file lock.
if (just_got_lock) {
const int kMaxTimeoutMS = 5000; // Matches windows.
const int kSleepPerTryMS = 200;
CHECK(!file_lock_);
file_lock_ = [[NSDistributedLock alloc] initWithPath:lock_filename];
BOOL got_file_lock = NO;
int elapsedMS = 0;
while (!(got_file_lock = [file_lock_ tryLock]) &&
elapsedMS < kMaxTimeoutMS) {
usleep(kSleepPerTryMS * 1000);
elapsedMS += kSleepPerTryMS;
}
if (!got_file_lock) {
[file_lock_ release];
file_lock_ = nil;
return false;
}
return true;
} else {
return file_lock_ != nil;
}
}
void RecursiveCrossProcessLock::ReleaseLock() {
if (file_lock_) {
[file_lock_ unlock];
[file_lock_ release];
file_lock_ = nil;
}
locking_thread_ = 0;
pthread_mutex_unlock(&recursive_lock_);
}
// This is set during test execution, to write RLZ files into a temporary
// directory instead of the user's Application Support folder.
NSString* g_test_folder;
// RlzValueStoreMac keeps its data in memory and only writes it to disk when
// ScopedRlzValueStoreLock goes out of scope. Hence, if several
// ScopedRlzValueStoreLocks are nested, they all need to use the same store
// object.
// This counts the nesting depth.
int g_lock_depth = 0;
// This is the store object that might be shared. Only set if g_lock_depth > 0.
RlzValueStoreMac* g_store_object = NULL;
NSString* CreateRlzDirectory() {
NSFileManager* manager = [NSFileManager defaultManager];
NSArray* paths = NSSearchPathForDirectoriesInDomains(
NSApplicationSupportDirectory, NSUserDomainMask, /*expandTilde=*/YES);
NSString* folder = nil;
if ([paths count] > 0)
folder = ObjCCast<NSString>([paths objectAtIndex:0]);
if (!folder)
folder = [@"~/Library/Application Support" stringByStandardizingPath];
folder = [folder stringByAppendingPathComponent:@"Google/RLZ"];
if (g_test_folder)
folder = [g_test_folder stringByAppendingPathComponent:folder];
[manager createDirectoryAtPath:folder
withIntermediateDirectories:YES
attributes:nil
error:nil];
return folder;
}
// Returns the path of the rlz plist store, also creates the parent directory
// path if it doesn't exist.
NSString* RlzPlistFilename() {
NSString* const kRlzFile = @"RlzStore.plist";
return [CreateRlzDirectory() stringByAppendingPathComponent:kRlzFile];
}
// Returns the path of the rlz lock file, also creates the parent directory
// path if it doesn't exist.
NSString* RlzLockFilename() {
NSString* const kRlzFile = @"lockfile";
return [CreateRlzDirectory() stringByAppendingPathComponent:kRlzFile];
}
} // namespace
ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() {
bool got_distributed_lock =
g_recursive_lock.TryGetCrossProcessLock(RlzLockFilename());
// At this point, we hold the in-process lock, no matter the value of
// |got_distributed_lock|.
++g_lock_depth;
if (!got_distributed_lock) {
// Give up. |store_| isn't set, which signals to callers that acquiring
// the lock failed. |g_recursive_lock| will be released by the
// destructor.
CHECK(!g_store_object);
return;
}
if (g_lock_depth > 1) {
// Reuse the already existing store object.
CHECK(g_store_object);
store_.reset(g_store_object);
return;
}
CHECK(!g_store_object);
NSString* plist = RlzPlistFilename();
// Create an empty file if none exists yet.
NSFileManager* manager = [NSFileManager defaultManager];
if (![manager fileExistsAtPath:plist isDirectory:NULL])
[[NSDictionary dictionary] writeToFile:plist atomically:YES];
NSMutableDictionary* dict =
[NSMutableDictionary dictionaryWithContentsOfFile:plist];
VERIFY(dict);
if (dict) {
store_.reset(new RlzValueStoreMac(dict, plist));
g_store_object = (RlzValueStoreMac*)store_.get();
}
}
ScopedRlzValueStoreLock::~ScopedRlzValueStoreLock() {
--g_lock_depth;
CHECK(g_lock_depth >= 0);
if (g_lock_depth > 0) {
// Other locks are still using store_, don't free it yet.
ignore_result(store_.release());
return;
}
if (store_.get()) {
g_store_object = NULL;
NSDictionary* dict =
static_cast<RlzValueStoreMac*>(store_.get())->dictionary();
VERIFY([dict writeToFile:RlzPlistFilename() atomically:YES]);
}
// Check that "store_ set" => "file_lock acquired". The converse isn't true,
// for example if the rlz data file can't be read.
if (store_.get())
CHECK(g_recursive_lock.file_lock_);
if (!g_recursive_lock.file_lock_)
CHECK(!store_.get());
g_recursive_lock.ReleaseLock();
}
RlzValueStore* ScopedRlzValueStoreLock::GetStore() {
return store_.get();
}
namespace testing {
void SetRlzStoreDirectory(const FilePath& directory) {
base::mac::ScopedNSAutoreleasePool pool;
[g_test_folder release];
if (directory.empty()) {
g_test_folder = nil;
} else {
// Not Unsafe on OS X.
g_test_folder =
[[NSString alloc] initWithUTF8String:directory.AsUTF8Unsafe().c_str()];
}
}
} // namespace testing
} // namespace rlz_lib

151
rlz/rlz.gyp Normal file

@ -0,0 +1,151 @@
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
{
'variables': {
'chromium_code': 1,
'variables': {
# Set force_rlz_use_chrome_net to 1 to use chrome's network stack instead
# of win inet.
'force_rlz_use_chrome_net%': 0,
},
'conditions': [
['force_rlz_use_chrome_net or OS=="mac"', {
'rlz_use_chrome_net%': 1,
}, {
'rlz_use_chrome_net%': 0,
}],
],
},
'target_defaults': {
'include_dirs': [
'..',
],
},
'targets': [
{
'target_name': 'rlz_lib',
'type': 'static_library',
'include_dirs': [],
'dependencies': [
'../base/base.gyp:base',
'../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
],
'sources': [
'lib/assert.cc',
'lib/assert.h',
'lib/crc32.h',
'lib/crc32_wrapper.cc',
'lib/crc8.h',
'lib/crc8.cc',
'lib/financial_ping.cc',
'lib/financial_ping.h',
'lib/lib_values.cc',
'lib/machine_id.cc',
'lib/machine_id.h',
'lib/rlz_enums.h',
'lib/rlz_lib.cc',
'lib/rlz_lib.h',
'lib/rlz_lib_clear.cc',
'lib/lib_values.h',
'lib/rlz_value_store.h',
'lib/string_utils.cc',
'lib/string_utils.h',
'mac/lib/machine_id_mac.cc',
'mac/lib/rlz_value_store_mac.mm',
'mac/lib/rlz_value_store_mac.h',
'win/lib/lib_mutex.cc',
'win/lib/lib_mutex.h',
'win/lib/machine_deal.cc',
'win/lib/machine_deal.h',
'win/lib/machine_id_win.cc',
'win/lib/process_info.cc',
'win/lib/process_info.h',
'win/lib/registry_util.cc',
'win/lib/registry_util.h',
'win/lib/rlz_lib.h',
'win/lib/rlz_lib_win.cc',
'win/lib/rlz_value_store_registry.cc',
'win/lib/rlz_value_store_registry.h',
'win/lib/vista_winnt.h',
],
'conditions': [
['rlz_use_chrome_net==1', {
'defines': [
'RLZ_NETWORK_IMPLEMENTATION_CHROME_NET',
],
'direct_dependent_settings': {
'defines': [
'RLZ_NETWORK_IMPLEMENTATION_CHROME_NET',
],
},
'dependencies': [
'../build/temp_gyp/googleurl.gyp:googleurl',
'../net/net.gyp:net',
],
}, {
'defines': [
'RLZ_NETWORK_IMPLEMENTATION_WIN_INET',
],
'direct_dependent_settings': {
'defines': [
'RLZ_NETWORK_IMPLEMENTATION_WIN_INET',
],
},
}],
],
},
{
'target_name': 'rlz_unittests',
'type': 'executable',
'include_dirs': [],
'dependencies': [
':rlz_lib',
'../base/base.gyp:base',
'../testing/gmock.gyp:gmock',
'../testing/gtest.gyp:gtest',
'../third_party/zlib/zlib.gyp:zlib',
],
'sources': [
'lib/crc32_unittest.cc',
'lib/crc8_unittest.cc',
'lib/financial_ping_test.cc',
'lib/lib_values_unittest.cc',
'lib/machine_id_unittest.cc',
'lib/rlz_lib_test.cc',
'lib/string_utils_unittest.cc',
'test/rlz_test_helpers.cc',
'test/rlz_test_helpers.h',
'test/rlz_unittest_main.cc',
'win/lib/machine_deal_test.cc',
],
'conditions': [
['rlz_use_chrome_net==1', {
'dependencies': [
'../net/net.gyp:net_test_support',
],
}]
],
},
],
'conditions': [
['OS=="win"', {
'targets': [
{
'target_name': 'rlz',
'type': 'shared_library',
'include_dirs': [],
'sources': [
'win/dll/dll_main.cc',
'win/dll/exports.cc',
],
'dependencies': [
':rlz_lib',
'../third_party/zlib/zlib.gyp:zlib',
],
},
],
}],
],
}

@ -0,0 +1,86 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Main entry point for all unit tests.
#include "rlz_test_helpers.h"
#include "rlz/lib/rlz_lib.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_WIN)
#include <shlwapi.h>
#include "base/win/registry.h"
#include "rlz/win/lib/rlz_lib.h"
#elif defined(OS_MACOSX)
#include "base/file_path.h"
#include "rlz/lib/rlz_value_store.h"
#endif
#if defined(OS_WIN)
namespace {
const wchar_t* kHKCUReplacement = L"Software\\Google\\RlzUtilUnittest\\HKCU";
const wchar_t* kHKLMReplacement = L"Software\\Google\\RlzUtilUnittest\\HKLM";
void OverrideRegistryHives() {
// Wipe the keys we redirect to.
// This gives us a stable run, even in the presence of previous
// crashes or failures.
LSTATUS err = SHDeleteKey(HKEY_CURRENT_USER, kHKCUReplacement);
EXPECT_TRUE(err == ERROR_SUCCESS || err == ERROR_FILE_NOT_FOUND);
err = SHDeleteKey(HKEY_CURRENT_USER, kHKLMReplacement);
EXPECT_TRUE(err == ERROR_SUCCESS || err == ERROR_FILE_NOT_FOUND);
// Create the keys we're redirecting HKCU and HKLM to.
base::win::RegKey hkcu;
base::win::RegKey hklm;
ASSERT_EQ(ERROR_SUCCESS,
hkcu.Create(HKEY_CURRENT_USER, kHKCUReplacement, KEY_READ));
ASSERT_EQ(ERROR_SUCCESS,
hklm.Create(HKEY_CURRENT_USER, kHKLMReplacement, KEY_READ));
rlz_lib::InitializeTempHivesForTesting(hklm, hkcu);
// And do the switcharoo.
ASSERT_EQ(ERROR_SUCCESS,
::RegOverridePredefKey(HKEY_CURRENT_USER, hkcu.Handle()));
ASSERT_EQ(ERROR_SUCCESS,
::RegOverridePredefKey(HKEY_LOCAL_MACHINE, hklm.Handle()));
}
void UndoOverrideRegistryHives() {
// Undo the redirection.
EXPECT_EQ(ERROR_SUCCESS, ::RegOverridePredefKey(HKEY_CURRENT_USER, NULL));
EXPECT_EQ(ERROR_SUCCESS, ::RegOverridePredefKey(HKEY_LOCAL_MACHINE, NULL));
}
} // namespace
#endif // defined(OS_WIN)
void RlzLibTestNoMachineState::SetUp() {
#if defined(OS_WIN)
OverrideRegistryHives();
#elif defined(OS_MACOSX)
base::mac::ScopedNSAutoreleasePool pool;
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
rlz_lib::testing::SetRlzStoreDirectory(temp_dir_.path());
#endif // defined(OS_WIN)
}
void RlzLibTestNoMachineState::TearDown() {
#if defined(OS_WIN)
UndoOverrideRegistryHives();
#elif defined(OS_MACOSX)
rlz_lib::testing::SetRlzStoreDirectory(FilePath());
#endif // defined(OS_WIN)
}
void RlzLibTestBase::SetUp() {
RlzLibTestNoMachineState::SetUp();
#if defined(OS_WIN)
rlz_lib::CreateMachineState();
#endif // defined(OS_WIN)
}

@ -0,0 +1,33 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Helper functions used by the tests.
#ifndef RLZ_TEST_RLZ_TEST_HELPERS_H
#define RLZ_TEST_RLZ_TEST_HELPERS_H
#include "base/compiler_specific.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_MACOSX)
#include "base/scoped_temp_dir.h"
#endif
class RlzLibTestNoMachineState : public ::testing::Test {
protected:
virtual void SetUp() OVERRIDE;
virtual void TearDown() OVERRIDE;
#if defined(OS_MACOSX)
ScopedTempDir temp_dir_;
#endif
};
class RlzLibTestBase : public RlzLibTestNoMachineState {
virtual void SetUp() OVERRIDE;
};
#endif // RLZ_TEST_RLZ_TEST_HELPERS_H

@ -0,0 +1,29 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Main entry point for all unit tests.
#include "base/at_exit.h"
#include "base/command_line.h"
#include "rlz/lib/rlz_lib.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
int main(int argc, char **argv) {
base::AtExitManager at_exit;
CommandLine::Init(argc, argv);
testing::InitGoogleMock(&argc, argv);
testing::InitGoogleTest(&argc, argv);
int ret = RUN_ALL_TESTS();
if (ret == 0) {
// Now re-run all the tests using a supplementary brand code. This brand
// code will remain in effect for the lifetime of the branding object.
rlz_lib::SupplementaryBranding branding("TEST");
ret = RUN_ALL_TESTS();
}
return ret;
}

15
rlz/win/dll/dll_main.cc Normal file

@ -0,0 +1,15 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Main function for the RLZ DLL.
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
#include <windows.h>
BOOL APIENTRY DllMain(HANDLE module, DWORD reason, LPVOID reserved) {
return TRUE;
}

108
rlz/win/dll/exports.cc Normal file

@ -0,0 +1,108 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Functions exported by the RLZ DLL.
#include "rlz/win/lib/rlz_lib.h"
#define RLZ_DLL_EXPORT extern "C" __declspec(dllexport)
RLZ_DLL_EXPORT bool RecordProductEvent(rlz_lib::Product product,
rlz_lib::AccessPoint point,
rlz_lib::Event event_id) {
return rlz_lib::RecordProductEvent(product, point, event_id);
}
RLZ_DLL_EXPORT bool GetProductEventsAsCgi(rlz_lib::Product product,
char* unescaped_cgi,
size_t unescaped_cgi_size) {
return rlz_lib::GetProductEventsAsCgi(product, unescaped_cgi,
unescaped_cgi_size);
}
RLZ_DLL_EXPORT bool ClearAllProductEvents(rlz_lib::Product product) {
return rlz_lib::ClearAllProductEvents(product);
}
RLZ_DLL_EXPORT bool ClearProductEvent(rlz_lib::Product product,
rlz_lib::AccessPoint point,
rlz_lib::Event event_id) {
return rlz_lib::ClearProductEvent(product, point, event_id);
}
RLZ_DLL_EXPORT bool GetAccessPointRlz(rlz_lib::AccessPoint point,
char* rlz,
size_t rlz_size) {
return rlz_lib::GetAccessPointRlz(point, rlz, rlz_size);
}
RLZ_DLL_EXPORT bool SetAccessPointRlz(rlz_lib::AccessPoint point,
const char* new_rlz) {
return rlz_lib::SetAccessPointRlz(point, new_rlz);
}
RLZ_DLL_EXPORT bool CreateMachineState() {
return rlz_lib::CreateMachineState();
}
RLZ_DLL_EXPORT bool SetMachineDealCode(const char* dcc) {
return rlz_lib::SetMachineDealCode(dcc);
}
RLZ_DLL_EXPORT bool GetMachineDealCodeAsCgi(char* cgi, size_t cgi_size) {
return rlz_lib::GetMachineDealCodeAsCgi(cgi, cgi_size);
}
RLZ_DLL_EXPORT bool GetMachineDealCode2(char* dcc, size_t dcc_size) {
return rlz_lib::GetMachineDealCode(dcc, dcc_size);
}
RLZ_DLL_EXPORT bool GetPingParams(rlz_lib::Product product,
const rlz_lib::AccessPoint* access_points,
char* unescaped_cgi,
size_t unescaped_cgi_size) {
return rlz_lib::GetPingParams(product, access_points, unescaped_cgi,
unescaped_cgi_size);
}
RLZ_DLL_EXPORT bool ParsePingResponse(rlz_lib::Product product,
const char* response) {
return rlz_lib::ParsePingResponse(product, response);
}
RLZ_DLL_EXPORT bool IsPingResponseValid(const char* response,
int* checksum_idx) {
return rlz_lib::IsPingResponseValid(response, checksum_idx);
}
RLZ_DLL_EXPORT bool SetMachineDealCodeFromPingResponse(const char* response) {
return rlz_lib::SetMachineDealCodeFromPingResponse(response);
}
RLZ_DLL_EXPORT bool SendFinancialPing(rlz_lib::Product product,
const rlz_lib::AccessPoint* access_points,
const char* product_signature,
const char* product_brand,
const char* product_id,
const char* product_lang,
bool exclude_machine_id) {
return rlz_lib::SendFinancialPing(product, access_points, product_signature,
product_brand, product_id, product_lang, exclude_machine_id);
}
RLZ_DLL_EXPORT bool SendFinancialPingNoDelay(
rlz_lib::Product product,
const rlz_lib::AccessPoint* access_points,
const char* product_signature,
const char* product_brand,
const char* product_id,
const char* product_lang,
bool exclude_machine_id) {
return rlz_lib::SendFinancialPing(product, access_points, product_signature,
product_brand, product_id, product_lang, exclude_machine_id, true);
}
RLZ_DLL_EXPORT void ClearProductState(
rlz_lib::Product product, const rlz_lib::AccessPoint* access_points) {
return rlz_lib::ClearProductState(product, access_points);
}

67
rlz/win/lib/lib_mutex.cc Normal file

@ -0,0 +1,67 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Mutex to guarantee serialization of RLZ key accesses.
#include "rlz/win/lib/lib_mutex.h"
#include <windows.h>
#include <Sddl.h> // For SDDL_REVISION_1, ConvertStringSecurityDescript..
#include <Aclapi.h> // For SetSecurityInfo
#include "base/logging.h"
#include "base/win/windows_version.h"
namespace {
const wchar_t kMutexName[] = L"{A946A6A9-917E-4949-B9BC-6BADA8C7FD63}";
} // namespace anonymous
namespace rlz_lib {
// Needed to allow synchronization across integrity levels.
static bool SetObjectToLowIntegrity(HANDLE object,
SE_OBJECT_TYPE type = SE_KERNEL_OBJECT) {
if (base::win::GetVersion() < base::win::VERSION_VISTA)
return true; // Not needed on XP.
// The LABEL_SECURITY_INFORMATION SDDL SACL to be set for low integrity.
static const wchar_t kLowIntegritySddlSacl[] = L"S:(ML;;NW;;;LW)";
bool result = false;
DWORD error = ERROR_SUCCESS;
PSECURITY_DESCRIPTOR security_descriptor = NULL;
PACL sacl = NULL;
BOOL sacl_present = FALSE;
BOOL sacl_defaulted = FALSE;
if (ConvertStringSecurityDescriptorToSecurityDescriptorW(
kLowIntegritySddlSacl, SDDL_REVISION_1, &security_descriptor, NULL)) {
if (GetSecurityDescriptorSacl(security_descriptor, &sacl_present,
&sacl, &sacl_defaulted)) {
error = SetSecurityInfo(object, type, LABEL_SECURITY_INFORMATION,
NULL, NULL, NULL, sacl);
result = (ERROR_SUCCESS == error);
}
LocalFree(security_descriptor);
}
return result;
}
LibMutex::LibMutex() : acquired_(false), mutex_(NULL) {
mutex_ = CreateMutex(NULL, false, kMutexName);
bool result = SetObjectToLowIntegrity(mutex_);
if (result) {
acquired_ = (WAIT_OBJECT_0 == WaitForSingleObject(mutex_, 5000L));
}
}
LibMutex::~LibMutex() {
if (acquired_) ReleaseMutex(mutex_);
CloseHandle(mutex_);
}
} // namespace rlz_lib

28
rlz/win/lib/lib_mutex.h Normal file

@ -0,0 +1,28 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Mutex to guarantee serialization of RLZ key accesses.
#ifndef RLZ_WIN_LIB_LIB_MUTEX_H_
#define RLZ_WIN_LIB_LIB_MUTEX_H_
#include <windows.h>
namespace rlz_lib {
class LibMutex {
public:
LibMutex();
~LibMutex();
bool failed(void) { return !acquired_; }
private:
bool acquired_;
HANDLE mutex_;
};
} // namespace rlz_lib
#endif // RLZ_WIN_LIB_LIB_MUTEX_H_

300
rlz/win/lib/machine_deal.cc Normal file

@ -0,0 +1,300 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Library functions related to the OEM Deal Confirmation Code.
#include "rlz/win/lib/machine_deal.h"
#include <windows.h>
#include <vector>
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/string_split.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/win/registry.h"
#include "rlz/lib/assert.h"
#include "rlz/lib/lib_values.h"
#include "rlz/win/lib/lib_mutex.h"
#include "rlz/win/lib/registry_util.h"
#include "rlz/win/lib/rlz_value_store_registry.h"
const wchar_t kDccValueName[] = L"DCC";
namespace {
// Current DCC can only uses [a-zA-Z0-9_-!@$*();.<>,:]
// We will be more liberal and allow some additional chars, but not url meta
// chars.
bool IsGoodDccChar(char ch) {
if (IsAsciiAlpha(ch) || IsAsciiDigit(ch))
return true;
switch (ch) {
case '_':
case '-':
case '!':
case '@':
case '$':
case '*':
case '(':
case ')':
case ';':
case '.':
case '<':
case '>':
case ',':
case ':':
return true;
}
return false;
}
// This function will remove bad rlz chars and also limit the max rlz to some
// reasonable size. It also assumes that normalized_dcc is at least
// kMaxDccLength+1 long.
void NormalizeDcc(const char* raw_dcc, char* normalized_dcc) {
int index = 0;
for (; raw_dcc[index] != 0 && index < rlz_lib::kMaxDccLength; ++index) {
char current = raw_dcc[index];
if (IsGoodDccChar(current)) {
normalized_dcc[index] = current;
} else {
normalized_dcc[index] = '.';
}
}
normalized_dcc[index] = 0;
}
bool GetResponseLine(const char* response_text, int response_length,
int* search_index, std::string* response_line) {
if (!response_line || !search_index || *search_index > response_length)
return false;
response_line->clear();
if (*search_index < 0)
return false;
int line_begin = *search_index;
const char* line_end = strchr(response_text + line_begin, '\n');
if (line_end == NULL || line_end - response_text > response_length) {
line_end = response_text + response_length;
*search_index = -1;
} else {
*search_index = line_end - response_text + 1;
}
response_line->assign(response_text + line_begin,
line_end - response_text - line_begin);
return true;
}
bool GetResponseValue(const std::string& response_line,
const std::string& response_key,
std::string* value) {
if (!value)
return false;
value->clear();
if (!StartsWithASCII(response_line, response_key, true))
return false;
std::vector<std::string> tokens;
base::SplitString(response_line, ':', &tokens);
if (tokens.size() != 2)
return false;
// The first token is the key, the second is the value. The value is already
// trimmed for whitespace.
*value = tokens[1];
return true;
}
} // namespace anonymous
namespace rlz_lib {
bool MachineDealCode::Set(const char* dcc) {
LibMutex lock;
if (lock.failed())
return false;
// TODO: if (!ProcessInfo::CanWriteMachineKey()) return false;
// Validate the new dcc value.
size_t length = strlen(dcc);
if (length > kMaxDccLength) {
ASSERT_STRING("MachineDealCode::Set: DCC length is exceeds max allowed.");
return false;
}
base::win::RegKey hklm_key(HKEY_LOCAL_MACHINE,
RlzValueStoreRegistry::GetWideLibKeyName().c_str(),
KEY_READ | KEY_WRITE | KEY_WOW64_32KEY);
if (!hklm_key.Valid()) {
ASSERT_STRING("MachineDealCode::Set: Unable to create / open machine key."
" Did you call rlz_lib::CreateMachineState()?");
return false;
}
char normalized_dcc[kMaxDccLength + 1];
NormalizeDcc(dcc, normalized_dcc);
VERIFY(length == strlen(normalized_dcc));
// Write the DCC to HKLM. Note that we need to include the null character
// when writing the string.
if (!RegKeyWriteValue(hklm_key, kDccValueName, normalized_dcc)) {
ASSERT_STRING("MachineDealCode::Set: Could not write the DCC value");
return false;
}
return true;
}
bool MachineDealCode::GetNewCodeFromPingResponse(const char* response,
bool* has_new_dcc, char* new_dcc, int new_dcc_size) {
if (!has_new_dcc || !new_dcc || !new_dcc_size)
return false;
*has_new_dcc = false;
new_dcc[0] = 0;
int response_length = -1;
if (!IsPingResponseValid(response, &response_length))
return false;
// Get the current DCC value to compare to later)
char stored_dcc[kMaxDccLength + 1];
if (!Get(stored_dcc, arraysize(stored_dcc)))
stored_dcc[0] = 0;
int search_index = 0;
std::string response_line;
std::string new_dcc_value;
bool old_dcc_confirmed = false;
const std::string dcc_cgi(kDccCgiVariable);
const std::string dcc_cgi_response(kSetDccResponseVariable);
while (GetResponseLine(response, response_length, &search_index,
&response_line)) {
std::string value;
if (!old_dcc_confirmed &&
GetResponseValue(response_line, dcc_cgi, &value)) {
// This is the old DCC confirmation - should match value in registry.
if (value != stored_dcc)
return false; // Corrupted DCC - ignore this response.
else
old_dcc_confirmed = true;
continue;
}
if (!(*has_new_dcc) &&
GetResponseValue(response_line, dcc_cgi_response, &value)) {
// This is the new DCC.
if (value.size() > kMaxDccLength) continue; // Too long
*has_new_dcc = true;
new_dcc_value = value;
}
}
old_dcc_confirmed |= (NULL == stored_dcc[0]);
base::strlcpy(new_dcc, new_dcc_value.c_str(), new_dcc_size);
return old_dcc_confirmed;
}
bool MachineDealCode::SetFromPingResponse(const char* response) {
bool has_new_dcc = false;
char new_dcc[kMaxDccLength + 1];
bool response_valid = GetNewCodeFromPingResponse(
response, &has_new_dcc, new_dcc, arraysize(new_dcc));
if (response_valid && has_new_dcc)
return Set(new_dcc);
return response_valid;
}
bool MachineDealCode::GetAsCgi(char* cgi, int cgi_size) {
if (!cgi || cgi_size <= 0) {
ASSERT_STRING("MachineDealCode::GetAsCgi: Invalid buffer");
return false;
}
cgi[0] = 0;
std::string cgi_arg;
base::StringAppendF(&cgi_arg, "%s=", kDccCgiVariable);
int cgi_arg_length = cgi_arg.size();
if (cgi_arg_length >= cgi_size) {
ASSERT_STRING("MachineDealCode::GetAsCgi: Insufficient buffer size");
return false;
}
base::strlcpy(cgi, cgi_arg.c_str(), cgi_size);
if (!Get(cgi + cgi_arg_length, cgi_size - cgi_arg_length)) {
cgi[0] = 0;
return false;
}
return true;
}
bool MachineDealCode::Get(char* dcc, int dcc_size) {
LibMutex lock;
if (lock.failed())
return false;
if (!dcc || dcc_size <= 0) {
ASSERT_STRING("MachineDealCode::Get: Invalid buffer");
return false;
}
dcc[0] = 0;
base::win::RegKey dcc_key(HKEY_LOCAL_MACHINE,
RlzValueStoreRegistry::GetWideLibKeyName().c_str(),
KEY_READ | KEY_WOW64_32KEY);
if (!dcc_key.Valid())
return false; // no DCC key.
size_t size = dcc_size;
if (!RegKeyReadValue(dcc_key, kDccValueName, dcc, &size)) {
ASSERT_STRING("MachineDealCode::Get: Insufficient buffer size");
dcc[0] = 0;
return false;
}
return true;
}
bool MachineDealCode::Clear() {
base::win::RegKey dcc_key(HKEY_LOCAL_MACHINE,
RlzValueStoreRegistry::GetWideLibKeyName().c_str(),
KEY_READ | KEY_WRITE | KEY_WOW64_32KEY);
if (!dcc_key.Valid())
return false; // no DCC key.
dcc_key.DeleteValue(kDccValueName);
// Verify deletion.
wchar_t dcc[kMaxDccLength + 1];
DWORD dcc_size = arraysize(dcc);
if (dcc_key.ReadValue(kDccValueName, dcc, &dcc_size, NULL) == ERROR_SUCCESS) {
ASSERT_STRING("MachineDealCode::Clear: Could not delete the DCC value.");
return false;
}
return true;
}
} // namespace rlz_lib

@ -0,0 +1,61 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Library functions related to the OEM Deal Confirmation Code.
#ifndef RLZ_WIN_LIB_MACHINE_DEAL_H_
#define RLZ_WIN_LIB_MACHINE_DEAL_H_
#include <string>
#include "rlz/win/lib/rlz_lib.h"
namespace rlz_lib {
class MachineDealCode {
public:
// Set the OEM Deal Confirmation Code (DCC). This information is used for RLZ
// initalization. Must have write access to HKLM - SYSTEM or admin, unless
// rlz_lib::CreateMachineState() has been successfully called.
static bool Set(const char* dcc);
// Get the OEM Deal Confirmation Code from the registry. Used to ping
// the server.
static bool Get(AccessPoint point,
char* dcc,
int dcc_size,
const wchar_t* sid = NULL);
// Parses a ping response, checks if it is valid and sets the machine DCC
// from the response. The response should also contain the current value of
// the DCC to be considered valid.
// Write access to HKLM (system / admin) needed, unless
// rlz_lib::CreateMachineState() has been successfully called.
static bool SetFromPingResponse(const char* response);
// Gets the new DCC to set from a ping response. Returns true if the ping
// response is valid. Sets has_new_dcc true if there is a new DCC value.
static bool GetNewCodeFromPingResponse(const char* response,
bool* has_new_dcc,
char* new_dcc,
int new_dcc_size);
// Get the DCC cgi argument string to append to a daily or financial ping.
static bool GetAsCgi(char* cgi, int cgi_size);
// Get the machine code.
static bool Get(char* dcc, int dcc_size);
protected:
// Clear the DCC value. Only for testing purposes.
// Requires write access to HKLM, unless rlz_lib::CreateMachineState() has
// been successfully called.
static bool Clear();
MachineDealCode() {}
~MachineDealCode() {}
};
} // namespace rlz_lib
#endif // RLZ_WIN_LIB_MACHINE_DEAL_H_

@ -0,0 +1,156 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// A test application for the MachineDealCode class.
//
// These tests should not be executed on the build server:
// - They assert for the failed cases.
// - They modify machine state (registry).
//
// These tests require write access to HKLM and HKCU, unless
// rlz_lib::CreateMachineState() has been successfully called.
#include "base/logging.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "rlz/test/rlz_test_helpers.h"
#include "rlz/win/lib/machine_deal.h"
class MachineDealCodeHelper : public rlz_lib::MachineDealCode {
public:
static bool Clear() { return rlz_lib::MachineDealCode::Clear(); }
private:
MachineDealCodeHelper() {}
~MachineDealCodeHelper() {}
};
class MachineDealCodeTest : public RlzLibTestBase {
};
TEST_F(MachineDealCodeTest, CreateMachineState) {
EXPECT_TRUE(rlz_lib::CreateMachineState());
}
TEST_F(MachineDealCodeTest, Set) {
MachineDealCodeHelper::Clear();
char dcc_50[50];
dcc_50[0] = 0;
EXPECT_TRUE(rlz_lib::MachineDealCode::Set("dcc_value"));
EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50));
EXPECT_STREQ("dcc_value", dcc_50);
EXPECT_TRUE(rlz_lib::MachineDealCode::Set("dcc_value_2"));
EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50));
EXPECT_STREQ("dcc_value_2", dcc_50);
}
TEST_F(MachineDealCodeTest, Get) {
MachineDealCodeHelper::Clear();
char dcc_50[50], dcc_2[2];
dcc_50[0] = 0;
dcc_2[0] = 0;
EXPECT_FALSE(rlz_lib::MachineDealCode::Get(dcc_50, 50));
EXPECT_TRUE(rlz_lib::MachineDealCode::Set("dcc_value"));
EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50));
EXPECT_STREQ("dcc_value", dcc_50);
EXPECT_FALSE(rlz_lib::MachineDealCode::Get(dcc_2, 2));
}
TEST_F(MachineDealCodeTest, SetFromPingResponse) {
rlz_lib::MachineDealCode::Set("MyDCCode");
char dcc_50[50];
// Bad responses
char* kBadDccResponse =
"dcc: NotMyDCCode \r\n"
"set_dcc: NewDCCode\r\n"
"crc32: 1B4D6BB3";
EXPECT_FALSE(rlz_lib::MachineDealCode::SetFromPingResponse(
kBadDccResponse));
EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50));
EXPECT_STREQ("MyDCCode", dcc_50);
char* kBadCrcResponse =
"dcc: MyDCCode \r\n"
"set_dcc: NewDCCode\r\n"
"crc32: 90707106";
EXPECT_FALSE(rlz_lib::MachineDealCode::SetFromPingResponse(
kBadCrcResponse));
EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50));
EXPECT_STREQ("MyDCCode", dcc_50);
// Good responses
char* kMissingSetResponse =
"dcc: MyDCCode \r\n"
"crc32: 35F2E717";
EXPECT_TRUE(rlz_lib::MachineDealCode::SetFromPingResponse(
kMissingSetResponse));
EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50));
EXPECT_STREQ("MyDCCode", dcc_50);
char* kGoodResponse =
"dcc: MyDCCode \r\n"
"set_dcc: NewDCCode\r\n"
"crc32: C8540E02";
EXPECT_TRUE(rlz_lib::MachineDealCode::SetFromPingResponse(
kGoodResponse));
EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50));
EXPECT_STREQ("NewDCCode", dcc_50);
char* kGoodResponse2 =
"set_dcc: NewDCCode2 \r\n"
"dcc: NewDCCode \r\n"
"crc32: 60B6409A";
EXPECT_TRUE(rlz_lib::MachineDealCode::SetFromPingResponse(
kGoodResponse2));
EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50));
EXPECT_STREQ("NewDCCode2", dcc_50);
MachineDealCodeHelper::Clear();
char* kGoodResponse3 =
"set_dcc: NewDCCode \r\n"
"crc32: 374C1C47";
EXPECT_TRUE(rlz_lib::MachineDealCode::SetFromPingResponse(
kGoodResponse3));
EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50));
EXPECT_STREQ("NewDCCode", dcc_50);
MachineDealCodeHelper::Clear();
char* kGoodResponse4 =
"dcc: \r\n"
"set_dcc: NewDCCode \r\n"
"crc32: 0AB1FB39";
EXPECT_TRUE(rlz_lib::MachineDealCode::SetFromPingResponse(
kGoodResponse4));
EXPECT_TRUE(rlz_lib::MachineDealCode::Get(dcc_50, 50));
EXPECT_STREQ("NewDCCode", dcc_50);
}
TEST_F(MachineDealCodeTest, GetAsCgi) {
MachineDealCodeHelper::Clear();
char cgi_50[50], cgi_2[2];
cgi_50[0] = 0;
cgi_2[0] = 0;
EXPECT_FALSE(rlz_lib::MachineDealCode::GetAsCgi(cgi_50, 50));
EXPECT_STREQ("", cgi_50);
EXPECT_TRUE(rlz_lib::MachineDealCode::Set("dcc_value"));
EXPECT_TRUE(rlz_lib::MachineDealCode::GetAsCgi(cgi_50, 50));
EXPECT_STREQ("dcc=dcc_value", cgi_50);
EXPECT_FALSE(rlz_lib::MachineDealCode::GetAsCgi(cgi_2, 2));
}

@ -0,0 +1,130 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <windows.h>
#include <Sddl.h> // For ConvertSidToStringSidW.
#include <string>
#include "base/memory/scoped_ptr.h"
#include "base/string16.h"
#include "rlz/lib/assert.h"
namespace rlz_lib {
namespace {
bool GetSystemVolumeSerialNumber(int* number) {
if (!number)
return false;
*number = 0;
// Find the system root path (e.g: C:\).
wchar_t system_path[MAX_PATH + 1];
if (!GetSystemDirectoryW(system_path, MAX_PATH))
return false;
wchar_t* first_slash = wcspbrk(system_path, L"\\/");
if (first_slash != NULL)
*(first_slash + 1) = 0;
DWORD number_local = 0;
if (!GetVolumeInformationW(system_path, NULL, 0, &number_local, NULL, NULL,
NULL, 0))
return false;
*number = number_local;
return true;
}
bool GetComputerSid(const wchar_t* account_name, SID* sid, DWORD sid_size) {
static const DWORD kStartDomainLength = 128; // reasonable to start with
scoped_array<wchar_t> domain_buffer(new wchar_t[kStartDomainLength]);
DWORD domain_size = kStartDomainLength;
DWORD sid_dword_size = sid_size;
SID_NAME_USE sid_name_use;
BOOL success = ::LookupAccountNameW(NULL, account_name, sid,
&sid_dword_size, domain_buffer.get(),
&domain_size, &sid_name_use);
if (!success && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
// We could have gotten the insufficient buffer error because
// one or both of sid and szDomain was too small. Check for that
// here.
if (sid_dword_size > sid_size)
return false;
if (domain_size > kStartDomainLength)
domain_buffer.reset(new wchar_t[domain_size]);
success = ::LookupAccountNameW(NULL, account_name, sid, &sid_dword_size,
domain_buffer.get(), &domain_size,
&sid_name_use);
}
return success != FALSE;
}
std::wstring ConvertSidToString(SID* sid) {
std::wstring sid_string;
#if _WIN32_WINNT >= 0x500
wchar_t* sid_buffer = NULL;
if (ConvertSidToStringSidW(sid, &sid_buffer)) {
sid_string = sid_buffer;
LocalFree(sid_buffer);
}
#else
SID_IDENTIFIER_AUTHORITY* sia = ::GetSidIdentifierAuthority(sid);
if(sia->Value[0] || sia->Value[1]) {
base::SStringPrintf(
&sid_string, L"S-%d-0x%02hx%02hx%02hx%02hx%02hx%02hx",
SID_REVISION, (USHORT)sia->Value[0], (USHORT)sia->Value[1],
(USHORT)sia->Value[2], (USHORT)sia->Value[3], (USHORT)sia->Value[4],
(USHORT)sia->Value[5]);
} else {
ULONG authority = 0;
for (int i = 2; i < 6; ++i) {
authority <<= 8;
authority |= sia->Value[i];
}
base::SStringPrintf(&sid_string, L"S-%d-%lu", SID_REVISION, authority);
}
int sub_auth_count = *::GetSidSubAuthorityCount(sid);
for(int i = 0; i < sub_auth_count; ++i)
base::StringAppendF(&sid_string, L"-%lu", *::GetSidSubAuthority(sid, i));
#endif
return sid_string;
}
} // namespace
bool GetRawMachineId(string16* sid_string, int* volume_id) {
// Calculate the Windows SID.
wchar_t computer_name[MAX_COMPUTERNAME_LENGTH + 1] = {0};
DWORD size = arraysize(computer_name);
if (GetComputerNameW(computer_name, &size)) {
char sid_buffer[SECURITY_MAX_SID_SIZE];
SID* sid = reinterpret_cast<SID*>(sid_buffer);
if (GetComputerSid(computer_name, sid, SECURITY_MAX_SID_SIZE)) {
*sid_string = ConvertSidToString(sid);
}
}
// Get the system drive volume serial number.
*volume_id = 0;
if (!GetSystemVolumeSerialNumber(volume_id)) {
ASSERT_STRING("GetMachineId: Failed to retrieve volume serial number");
*volume_id = 0;
}
return true;
}
} // namespace rlz_lib

196
rlz/win/lib/process_info.cc Normal file

@ -0,0 +1,196 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Information about the current process.
#include "rlz/win/lib/process_info.h"
#include <windows.h>
#include <Sddl.h> // For ConvertSidToStringSid.
#include <LMCons.h> // For UNLEN
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/process_util.h"
#include "base/win/scoped_handle.h"
#include "base/win/windows_version.h"
#include "rlz/lib/assert.h"
#include "rlz/win/lib/vista_winnt.h"
namespace {
HRESULT GetCurrentUser(std::wstring* name,
std::wstring* domain,
std::wstring* sid) {
DWORD err;
// Get the current username & domain the hard way. (GetUserNameEx would be
// nice, but unfortunately requires connectivity to a domain controller.
// Useless.)
// (Following call doesn't work if running as a Service - because a Service
// runs under special accounts like LOCAL_SYSTEM, not as the logged in user.
// In which case, search for and use the process handle of a running
// Explorer.exe.)
HANDLE token;
if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token))
return E_FAIL;
base::win::ScopedHandle scoped_process_token(token);
// (Following call will fail with ERROR_INSUFFICIENT_BUFFER and give us the
// required size.)
scoped_array<char> token_user_bytes;
DWORD token_user_size;
DWORD token_user_size2;
BOOL result = ::GetTokenInformation(token, TokenUser, NULL, 0,
&token_user_size);
err = ::GetLastError();
CHECK(!result && err == ERROR_INSUFFICIENT_BUFFER);
token_user_bytes.reset(new char[token_user_size]);
if (!token_user_bytes.get())
return E_OUTOFMEMORY;
if (!::GetTokenInformation(token, TokenUser, token_user_bytes.get(),
token_user_size, &token_user_size2)) {
return E_FAIL;
}
WCHAR user_name[UNLEN + 1]; // max username length
WCHAR domain_name[UNLEN + 1];
DWORD user_name_size = UNLEN + 1;
DWORD domain_name_size = UNLEN + 1;
SID_NAME_USE sid_type;
TOKEN_USER* token_user =
reinterpret_cast<TOKEN_USER*>(token_user_bytes.get());
if (!token_user)
return E_FAIL;
PSID user_sid = token_user->User.Sid;
if (!::LookupAccountSidW(NULL, user_sid, user_name, &user_name_size,
domain_name, &domain_name_size, &sid_type)) {
return E_FAIL;
}
if (name != NULL) {
*name = user_name;
}
if (domain != NULL) {
*domain = domain_name;
}
if (sid != NULL) {
LPWSTR string_sid;
ConvertSidToStringSidW(user_sid, &string_sid);
*sid = string_sid; // copy out to cstring
// free memory, as documented for ConvertSidToStringSid
LocalFree(string_sid);
}
return S_OK;
}
HRESULT GetElevationType(PTOKEN_ELEVATION_TYPE elevation) {
if (!elevation)
return E_POINTER;
*elevation = TokenElevationTypeDefault;
if (base::win::GetVersion() < base::win::VERSION_VISTA)
return E_FAIL;
HANDLE process_token;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &process_token))
return HRESULT_FROM_WIN32(GetLastError());
base::win::ScopedHandle scoped_process_token(process_token);
DWORD size;
TOKEN_ELEVATION_TYPE elevation_type;
if (!GetTokenInformation(process_token, TokenElevationType, &elevation_type,
sizeof(elevation_type), &size)) {
return HRESULT_FROM_WIN32(GetLastError());
}
*elevation = elevation_type;
return S_OK;
}
// based on http://msdn2.microsoft.com/en-us/library/aa376389.aspx
bool GetUserGroup(long* group) {
if (!group)
return false;
*group = 0;
// groups are listed in DECREASING order of importance
// (eg. If a user is a member of both the admin group and
// the power user group, it is more useful to list the user
// as an admin)
DWORD user_groups[] = {DOMAIN_ALIAS_RID_ADMINS,
DOMAIN_ALIAS_RID_POWER_USERS};
SID_IDENTIFIER_AUTHORITY nt_authority = SECURITY_NT_AUTHORITY;
for (int i = 0; i < arraysize(user_groups) && *group == 0; ++i) {
PSID current_group;
if (AllocateAndInitializeSid(&nt_authority, 2,
SECURITY_BUILTIN_DOMAIN_RID,
user_groups[i], 0, 0, 0, 0,
0, 0, &current_group)) {
BOOL current_level;
if (CheckTokenMembership(NULL, current_group, &current_level) &&
current_level) {
*group = user_groups[i];
}
FreeSid(current_group);
}
}
return group != 0;
}
} //anonymous
namespace rlz_lib {
bool ProcessInfo::IsRunningAsSystem() {
static std::wstring name;
static std::wstring domain;
static std::wstring sid;
if (name.empty())
CHECK(SUCCEEDED(GetCurrentUser(&name, &domain, &sid)));
return (name == L"SYSTEM");
}
bool ProcessInfo::HasAdminRights() {
static bool evaluated = false;
static bool has_rights = false;
if (!evaluated) {
if (IsRunningAsSystem()) {
has_rights = true;
} else if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
TOKEN_ELEVATION_TYPE elevation;
base::IntegrityLevel level;
if (SUCCEEDED(GetElevationType(&elevation)) &&
base::GetProcessIntegrityLevel(base::GetCurrentProcessHandle(), &level))
has_rights = (elevation == TokenElevationTypeFull) ||
(level == HIGH_INTEGRITY);
} else {
long group = 0;
if (GetUserGroup(&group))
has_rights = (group == DOMAIN_ALIAS_RID_ADMINS);
}
}
evaluated = true;
if (!has_rights)
ASSERT_STRING("ProcessInfo::HasAdminRights: Does not have admin rights.");
return has_rights;
}
}; // namespace

@ -0,0 +1,32 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Information about the current process.
#ifndef RLZ_WIN_LIB_PROCESS_INFO_H_
#define RLZ_WIN_LIB_PROCESS_INFO_H_
#include "base/basictypes.h"
namespace rlz_lib {
class ProcessInfo {
public:
enum IntegrityLevel {
INTEGRITY_UNKNOWN,
LOW_INTEGRITY,
MEDIUM_INTEGRITY,
HIGH_INTEGRITY,
};
// All these functions cache the result after first run.
static bool IsRunningAsSystem();
static bool HasAdminRights(); // System / Admin / High Elevation on Vista
private:
DISALLOW_COPY_AND_ASSIGN(ProcessInfo);
}; // class
}; // namespace
#endif // RLZ_WIN_LIB_PROCESS_INFO_H_

@ -0,0 +1,77 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// A helper library to keep track of a user's key by SID.
// Used by RLZ libary. Also to be used by SearchWithGoogle library.
#include "rlz/win/lib/registry_util.h"
#include "base/process_util.h"
#include "base/utf_string_conversions.h"
#include "base/win/registry.h"
#include "base/win/windows_version.h"
#include "rlz/lib/assert.h"
#include "rlz/win/lib/process_info.h"
namespace rlz_lib {
bool RegKeyReadValue(base::win::RegKey& key, const wchar_t* name,
char* value, size_t* value_size) {
value[0] = 0;
std::wstring value_string;
if (key.ReadValue(name, &value_string) != ERROR_SUCCESS) {
return false;
}
if (value_string.length() > *value_size) {
*value_size = value_string.length();
return false;
}
// Note that RLZ string are always ASCII by design.
strncpy(value, WideToUTF8(value_string).c_str(), *value_size);
value[*value_size - 1] = 0;
return true;
}
bool RegKeyWriteValue(base::win::RegKey& key, const wchar_t* name,
const char* value) {
std::wstring value_string(ASCIIToWide(value));
return key.WriteValue(name, value_string.c_str()) == ERROR_SUCCESS;
}
bool HasUserKeyAccess(bool write_access) {
// The caller is trying to access HKEY_CURRENT_USER. Test to see if we can
// read from there. Don't try HKEY_CURRENT_USER because this will cause
// problems in the unit tests: if we open HKEY_CURRENT_USER directly here,
// the overriding done for unit tests will no longer work. So we try subkey
// "Software" which is known to always exist.
base::win::RegKey key;
if (key.Open(HKEY_CURRENT_USER, L"Software", KEY_READ) != ERROR_SUCCESS)
ASSERT_STRING("Could not open HKEY_CURRENT_USER");
if (ProcessInfo::IsRunningAsSystem()) {
ASSERT_STRING("UserKey::HasAccess: No access as SYSTEM without SID set.");
return false;
}
if (write_access) {
if (base::win::GetVersion() < base::win::VERSION_VISTA) return true;
base::ProcessHandle process_handle = base::GetCurrentProcessHandle();
base::IntegrityLevel level = base::INTEGRITY_UNKNOWN;
if (!base::GetProcessIntegrityLevel(process_handle, &level)) {
ASSERT_STRING("UserKey::HasAccess: Cannot determine Integrity Level.");
return false;
}
if (level <= base::LOW_INTEGRITY) {
ASSERT_STRING("UserKey::HasAccess: Cannot write from Low Integrity.");
return false;
}
}
return true;
}
} // namespace rlz_lib

@ -0,0 +1,29 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef RLZ_WIN_LIB_REGISTRY_UTIL_H_
#define RLZ_WIN_LIB_REGISTRY_UTIL_H_
namespace base {
namespace win {
class RegKey;
} // namespace win
} // namespace base
namespace rlz_lib {
bool RegKeyReadValue(base::win::RegKey& key,
const wchar_t* name,
char* value,
size_t* value_size);
bool RegKeyWriteValue(base::win::RegKey& key,
const wchar_t* name,
const char* value);
bool HasUserKeyAccess(bool write_access);
} // namespace rlz_lib
#endif // RLZ_WIN_LIB_REGISTRY_UTIL_H_

56
rlz/win/lib/rlz_lib.h Normal file

@ -0,0 +1,56 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// A library to manage RLZ information for access-points shared
// across different client applications.
//
// All functions return true on success and false on error.
// This implemenation is thread safe.
//
// Each prototype mentions the registry access requirements:
//
// HKLM read: Will work from any process and at any privilege level on Vista.
// HKCU read: Calls made from the SYSTEM account must pass the current user's
// SID as the optional 'sid' param. Can be called from low integrity
// process on Vista.
// HKCU write: Calls made from the SYSTEM account must pass the current user's
// SID as the optional 'sid' param. Calls require at least medium
// integrity on Vista (e.g. Toolbar will need to use their broker)
// HKLM write: Calls must be made from an account with admin rights. No SID
// need be passed when running as SYSTEM.
// Functions which do not access registry will be marked with "no restrictions".
#ifndef RLZ_WIN_LIB_RLZ_LIB_H_
#define RLZ_WIN_LIB_RLZ_LIB_H_
// Clients can get away by just including rlz/lib/rlz_lib.h. This file only
// contains function definitions for files used by tests. It's mostly kept
// around for backwards-compatibility.
#include "rlz/lib/rlz_lib.h"
#include "base/win/registry.h"
namespace rlz_lib {
#if defined(OS_WIN)
// Initialize temporary HKLM/HKCU registry hives used for testing.
// Testing RLZ requires reading and writing to the Windows registry. To keep
// the tests isolated from the machine's state, as well as to prevent the tests
// from causing side effects in the registry, HKCU and HKLM are overridden for
// the duration of the tests. RLZ tests don't expect the HKCU and KHLM hives to
// be empty though, and this function initializes the minimum value needed so
// that the test will run successfully.
//
// The two arguments to this function should be the keys that will represent
// the HKLM and HKCU registry hives during the tests. This function should be
// called *before* the hives are overridden.
void InitializeTempHivesForTesting(const base::win::RegKey& temp_hklm_key,
const base::win::RegKey& temp_hkcu_key);
#endif // defined(OS_WIN)
} // namespace rlz_lib
#endif // RLZ_WIN_LIB_RLZ_LIB_H_

260
rlz/win/lib/rlz_lib_win.cc Normal file

@ -0,0 +1,260 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// A library to manage RLZ information for access-points shared
// across different client applications.
#include "rlz/win/lib/rlz_lib.h"
#include <windows.h>
#include <aclapi.h>
#include <winerror.h>
#include "base/basictypes.h"
#include "base/win/registry.h"
#include "base/win/windows_version.h"
#include "rlz/lib/assert.h"
#include "rlz/lib/rlz_value_store.h"
#include "rlz/win/lib/machine_deal.h"
#include "rlz/win/lib/rlz_value_store_registry.h"
namespace {
// Path to recursively copy into the replacemment hives. These are needed
// to make sure certain win32 APIs continue to run correctly once the real
// hives are replaced.
const wchar_t* kHKLMAccessProviders =
L"System\\CurrentControlSet\\Control\\Lsa\\AccessProviders";
// Helper functions
void CopyRegistryTree(const base::win::RegKey& src, base::win::RegKey* dest) {
// First copy values.
for (base::win::RegistryValueIterator i(src.Handle(), L"");
i.Valid(); ++i) {
dest->WriteValue(i.Name(), reinterpret_cast<const void*>(i.Value()),
i.ValueSize(), i.Type());
}
// Next copy subkeys recursively.
for (base::win::RegistryKeyIterator i(src.Handle(), L"");
i.Valid(); ++i) {
base::win::RegKey subkey(dest->Handle(), i.Name(), KEY_ALL_ACCESS);
CopyRegistryTree(base::win::RegKey(src.Handle(), i.Name(), KEY_READ),
&subkey);
}
}
} // namespace anonymous
namespace rlz_lib {
// OEM Deal confirmation storage functions.
template<class T>
class typed_buffer_ptr {
scoped_array<char> buffer_;
public:
typed_buffer_ptr() {
}
explicit typed_buffer_ptr(size_t size) : buffer_(new char[size]) {
}
void reset(size_t size) {
buffer_.reset(new char[size]);
}
operator T*() {
return reinterpret_cast<T*>(buffer_.get());
}
};
// Check if this SID has the desired access by scanning the ACEs in the DACL.
// This function is part of the rlz_lib namespace so that it can be called from
// unit tests. Non-unit test code should not call this function.
bool HasAccess(PSID sid, ACCESS_MASK access_mask, ACL* dacl) {
if (dacl == NULL)
return false;
ACL_SIZE_INFORMATION info;
if (!GetAclInformation(dacl, &info, sizeof(info), AclSizeInformation))
return false;
GENERIC_MAPPING generic_mapping = {KEY_READ, KEY_WRITE, KEY_EXECUTE,
KEY_ALL_ACCESS};
MapGenericMask(&access_mask, &generic_mapping);
for (DWORD i = 0; i < info.AceCount; ++i) {
ACCESS_ALLOWED_ACE* ace;
if (GetAce(dacl, i, reinterpret_cast<void**>(&ace))) {
if ((ace->Header.AceFlags & INHERIT_ONLY_ACE) == INHERIT_ONLY_ACE)
continue;
PSID existing_sid = reinterpret_cast<PSID>(&ace->SidStart);
DWORD mask = ace->Mask;
MapGenericMask(&mask, &generic_mapping);
if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE &&
(mask & access_mask) == access_mask && EqualSid(existing_sid, sid))
return true;
if (ace->Header.AceType == ACCESS_DENIED_ACE_TYPE &&
(mask & access_mask) != 0 && EqualSid(existing_sid, sid))
return false;
}
}
return false;
}
bool CreateMachineState() {
LibMutex lock;
if (lock.failed())
return false;
base::win::RegKey hklm_key;
if (hklm_key.Create(HKEY_LOCAL_MACHINE,
RlzValueStoreRegistry::GetWideLibKeyName().c_str(),
KEY_ALL_ACCESS | KEY_WOW64_32KEY) != ERROR_SUCCESS) {
ASSERT_STRING("rlz_lib::CreateMachineState: "
"Unable to create / open machine key.");
return false;
}
// Create a SID that represents ALL USERS.
DWORD users_sid_size = SECURITY_MAX_SID_SIZE;
typed_buffer_ptr<SID> users_sid(users_sid_size);
CreateWellKnownSid(WinBuiltinUsersSid, NULL, users_sid, &users_sid_size);
// Get the security descriptor for the registry key.
DWORD original_sd_size = 0;
::RegGetKeySecurity(hklm_key.Handle(), DACL_SECURITY_INFORMATION, NULL,
&original_sd_size);
typed_buffer_ptr<SECURITY_DESCRIPTOR> original_sd(original_sd_size);
LONG result = ::RegGetKeySecurity(hklm_key.Handle(),
DACL_SECURITY_INFORMATION, original_sd, &original_sd_size);
if (result != ERROR_SUCCESS) {
ASSERT_STRING("rlz_lib::CreateMachineState: "
"Unable to create / open machine key.");
return false;
}
// Make a copy of the security descriptor so we can modify it. The one
// returned by RegGetKeySecurity() is self-relative, so we need to make it
// absolute.
DWORD new_sd_size = 0;
DWORD dacl_size = 0;
DWORD sacl_size = 0;
DWORD owner_size = 0;
DWORD group_size = 0;
::MakeAbsoluteSD(original_sd, NULL, &new_sd_size, NULL, &dacl_size,
NULL, &sacl_size, NULL, &owner_size,
NULL, &group_size);
typed_buffer_ptr<SECURITY_DESCRIPTOR> new_sd(new_sd_size);
// Make sure the DACL is big enough to add one more ACE.
typed_buffer_ptr<ACL> dacl(dacl_size + SECURITY_MAX_SID_SIZE);
typed_buffer_ptr<ACL> sacl(sacl_size);
typed_buffer_ptr<SID> owner(owner_size);
typed_buffer_ptr<SID> group(group_size);
if (!::MakeAbsoluteSD(original_sd, new_sd, &new_sd_size, dacl, &dacl_size,
sacl, &sacl_size, owner, &owner_size,
group, &group_size)) {
ASSERT_STRING("rlz_lib::CreateMachineState: MakeAbsoluteSD failed");
return false;
}
// If all users already have read/write access to the registry key, then
// nothing to do. Otherwise change the security descriptor of the key to
// give everyone access.
if (HasAccess(users_sid, KEY_ALL_ACCESS, dacl)) {
return false;
}
// Add ALL-USERS ALL-ACCESS ACL.
EXPLICIT_ACCESS ea;
ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
ea.grfAccessPermissions = GENERIC_ALL | KEY_ALL_ACCESS;
ea.grfAccessMode = GRANT_ACCESS;
ea.grfInheritance= SUB_CONTAINERS_AND_OBJECTS_INHERIT;
ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
ea.Trustee.ptstrName = L"Everyone";
ACL* new_dacl = NULL;
result = SetEntriesInAcl(1, &ea, dacl, &new_dacl);
if (result != ERROR_SUCCESS) {
ASSERT_STRING("rlz_lib::CreateMachineState: SetEntriesInAcl failed");
return false;
}
BOOL ok = SetSecurityDescriptorDacl(new_sd, TRUE, new_dacl, FALSE);
if (!ok) {
ASSERT_STRING("rlz_lib::CreateMachineState: "
"SetSecurityDescriptorOwner failed");
LocalFree(new_dacl);
return false;
}
result = ::RegSetKeySecurity(hklm_key.Handle(),
DACL_SECURITY_INFORMATION,
new_sd);
// Note that the new DACL cannot be freed until after the call to
// RegSetKeySecurity().
LocalFree(new_dacl);
bool success = true;
if (result != ERROR_SUCCESS) {
ASSERT_STRING("rlz_lib::CreateMachineState: "
"Unable to create / open machine key.");
success = false;
}
return success;
}
bool SetMachineDealCode(const char* dcc) {
return MachineDealCode::Set(dcc);
}
bool GetMachineDealCodeAsCgi(char* cgi, size_t cgi_size) {
return MachineDealCode::GetAsCgi(cgi, cgi_size);
}
bool GetMachineDealCode(char* dcc, size_t dcc_size) {
return MachineDealCode::Get(dcc, dcc_size);
}
// Combined functions.
bool SetMachineDealCodeFromPingResponse(const char* response) {
return MachineDealCode::SetFromPingResponse(response);
}
void InitializeTempHivesForTesting(const base::win::RegKey& temp_hklm_key,
const base::win::RegKey& temp_hkcu_key) {
// For the moment, the HKCU hive requires no initialization.
if (base::win::GetVersion() >= base::win::VERSION_WIN7) {
// Copy the following HKLM subtrees to the temporary location so that the
// win32 APIs used by the tests continue to work:
//
// HKLM\System\CurrentControlSet\Control\Lsa\AccessProviders
//
// This seems to be required since Win7.
base::win::RegKey dest(temp_hklm_key.Handle(), kHKLMAccessProviders,
KEY_ALL_ACCESS);
CopyRegistryTree(base::win::RegKey(HKEY_LOCAL_MACHINE,
kHKLMAccessProviders,
KEY_READ),
&dest);
}
}
} // namespace rlz_lib

@ -0,0 +1,384 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "rlz/win/lib/rlz_value_store_registry.h"
#include "base/win/registry.h"
#include "base/stringprintf.h"
#include "base/utf_string_conversions.h"
#include "rlz/lib/assert.h"
#include "rlz/lib/lib_values.h"
#include "rlz/lib/rlz_lib.h"
#include "rlz/lib/string_utils.h"
#include "rlz/win/lib/registry_util.h"
namespace rlz_lib {
namespace {
//
// Registry keys:
//
// RLZ's are stored as:
// <AccessPointName> = <RLZ value> @ kRootKey\kLibKeyName\kRlzsSubkeyName.
//
// Events are stored as:
// <AccessPointName><EventName> = 1 @
// HKCU\kLibKeyName\kEventsSubkeyName\GetProductName(product).
//
// The OEM Deal Confirmation Code (DCC) is stored as
// kDccValueName = <DCC value> @ HKLM\kLibKeyName
//
// The last ping time, per product is stored as:
// GetProductName(product) = <last ping time> @
// HKCU\kLibKeyName\kPingTimesSubkeyName.
//
// The server does not care about any of these constants.
//
const char kLibKeyName[] = "Software\\Google\\Common\\Rlz";
const wchar_t kGoogleKeyName[] = L"Software\\Google";
const wchar_t kGoogleCommonKeyName[] = L"Software\\Google\\Common";
const char kRlzsSubkeyName[] = "RLZs";
const char kEventsSubkeyName[] = "Events";
const char kStatefulEventsSubkeyName[] = "StatefulEvents";
const char kPingTimesSubkeyName[] = "PTimes";
std::wstring GetWideProductName(Product product) {
return ASCIIToWide(GetProductName(product));
}
void AppendBrandToString(std::string* str) {
std::string brand(SupplementaryBranding::GetBrand());
if (!brand.empty())
base::StringAppendF(str, "\\_%s", brand.c_str());
}
// Function to get the specific registry keys.
bool GetRegKey(const char* name, REGSAM access, base::win::RegKey* key) {
std::string key_location;
base::StringAppendF(&key_location, "%s\\%s", kLibKeyName, name);
AppendBrandToString(&key_location);
LONG ret = ERROR_SUCCESS;
if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK)) {
ret = key->Create(HKEY_CURRENT_USER, ASCIIToWide(key_location).c_str(),
access);
} else {
ret = key->Open(HKEY_CURRENT_USER, ASCIIToWide(key_location).c_str(),
access);
}
return ret == ERROR_SUCCESS;
}
bool GetPingTimesRegKey(REGSAM access, base::win::RegKey* key) {
return GetRegKey(kPingTimesSubkeyName, access, key);
}
bool GetEventsRegKey(const char* event_type,
const rlz_lib::Product* product,
REGSAM access, base::win::RegKey* key) {
std::string key_location;
base::StringAppendF(&key_location, "%s\\%s", kLibKeyName,
event_type);
AppendBrandToString(&key_location);
if (product != NULL) {
std::string product_name = GetProductName(*product);
if (product_name.empty())
return false;
base::StringAppendF(&key_location, "\\%s", product_name.c_str());
}
LONG ret = ERROR_SUCCESS;
if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK)) {
ret = key->Create(HKEY_CURRENT_USER, ASCIIToWide(key_location).c_str(),
access);
} else {
ret = key->Open(HKEY_CURRENT_USER, ASCIIToWide(key_location).c_str(),
access);
}
return ret == ERROR_SUCCESS;
}
bool GetAccessPointRlzsRegKey(REGSAM access, base::win::RegKey* key) {
return GetRegKey(kRlzsSubkeyName, access, key);
}
bool ClearAllProductEventValues(rlz_lib::Product product, const char* key) {
std::wstring product_name = GetWideProductName(product);
if (product_name.empty())
return false;
base::win::RegKey reg_key;
GetEventsRegKey(key, NULL, KEY_WRITE, &reg_key);
reg_key.DeleteKey(product_name.c_str());
// Verify that the value no longer exists.
base::win::RegKey product_events(
reg_key.Handle(), product_name.c_str(), KEY_READ);
if (product_events.Valid()) {
ASSERT_STRING("ClearAllProductEvents: Key deletion failed");
return false;
}
return true;
}
// Deletes a registry key if it exists and has no subkeys or values.
// TODO: Move this to a registry_utils file and add unittest.
bool DeleteKeyIfEmpty(HKEY root_key, const wchar_t* key_name) {
if (!key_name) {
ASSERT_STRING("DeleteKeyIfEmpty: key_name is NULL");
return false;
} else { // Scope needed for RegKey
base::win::RegKey key(root_key, key_name, KEY_READ);
if (!key.Valid())
return true; // Key does not exist - nothing to do.
base::win::RegistryKeyIterator key_iter(root_key, key_name);
if (key_iter.SubkeyCount() > 0)
return true; // Not empty, so nothing to do
base::win::RegistryValueIterator value_iter(root_key, key_name);
if (value_iter.ValueCount() > 0)
return true; // Not empty, so nothing to do
}
// The key is empty - delete it now.
base::win::RegKey key(root_key, L"", KEY_WRITE);
return key.DeleteKey(key_name) == ERROR_SUCCESS;
}
} // namespace
// static
std::wstring RlzValueStoreRegistry::GetWideLibKeyName() {
return ASCIIToWide(kLibKeyName);
}
bool RlzValueStoreRegistry::HasAccess(AccessType type) {
return HasUserKeyAccess(type == kWriteAccess);
}
bool RlzValueStoreRegistry::WritePingTime(Product product, int64 time) {
base::win::RegKey key;
std::wstring product_name = GetWideProductName(product);
return GetPingTimesRegKey(KEY_WRITE, &key) &&
key.WriteValue(product_name.c_str(), &time, sizeof(time),
REG_QWORD) == ERROR_SUCCESS;
}
bool RlzValueStoreRegistry::ReadPingTime(Product product, int64* time) {
base::win::RegKey key;
std::wstring product_name = GetWideProductName(product);
return GetPingTimesRegKey(KEY_READ, &key) &&
key.ReadInt64(product_name.c_str(), time) == ERROR_SUCCESS;
}
bool RlzValueStoreRegistry::ClearPingTime(Product product) {
base::win::RegKey key;
GetPingTimesRegKey(KEY_WRITE, &key);
std::wstring product_name = GetWideProductName(product);
key.DeleteValue(product_name.c_str());
// Verify deletion.
uint64 value;
DWORD size = sizeof(value);
if (key.ReadValue(
product_name.c_str(), &value, &size, NULL) == ERROR_SUCCESS) {
ASSERT_STRING("RlzValueStoreRegistry::ClearPingTime: Failed to delete.");
return false;
}
return true;
}
bool RlzValueStoreRegistry::WriteAccessPointRlz(AccessPoint access_point,
const char* new_rlz) {
const char* access_point_name = GetAccessPointName(access_point);
if (!access_point_name)
return false;
std::wstring access_point_name_wide(ASCIIToWide(access_point_name));
base::win::RegKey key;
GetAccessPointRlzsRegKey(KEY_WRITE, &key);
if (!RegKeyWriteValue(key, access_point_name_wide.c_str(), new_rlz)) {
ASSERT_STRING("SetAccessPointRlz: Could not write the new RLZ value");
return false;
}
return true;
}
bool RlzValueStoreRegistry::ReadAccessPointRlz(AccessPoint access_point,
char* rlz,
size_t rlz_size) {
const char* access_point_name = GetAccessPointName(access_point);
if (!access_point_name)
return false;
size_t size = rlz_size;
base::win::RegKey key;
GetAccessPointRlzsRegKey(KEY_READ, &key);
if (!RegKeyReadValue(key, ASCIIToWide(access_point_name).c_str(),
rlz, &size)) {
rlz[0] = 0;
if (size > rlz_size) {
ASSERT_STRING("GetAccessPointRlz: Insufficient buffer size");
return false;
}
}
return true;
}
bool RlzValueStoreRegistry::ClearAccessPointRlz(AccessPoint access_point) {
const char* access_point_name = GetAccessPointName(access_point);
if (!access_point_name)
return false;
std::wstring access_point_name_wide(ASCIIToWide(access_point_name));
base::win::RegKey key;
GetAccessPointRlzsRegKey(KEY_WRITE, &key);
key.DeleteValue(access_point_name_wide.c_str());
// Verify deletion.
DWORD value;
if (key.ReadValueDW(access_point_name_wide.c_str(), &value) ==
ERROR_SUCCESS) {
ASSERT_STRING("SetAccessPointRlz: Could not clear the RLZ value.");
return false;
}
return true;
}
bool RlzValueStoreRegistry::AddProductEvent(Product product,
const char* event_rlz) {
std::wstring event_rlz_wide(ASCIIToWide(event_rlz));
base::win::RegKey reg_key;
GetEventsRegKey(kEventsSubkeyName, &product, KEY_WRITE, &reg_key);
if (reg_key.WriteValue(event_rlz_wide.c_str(), 1) != ERROR_SUCCESS) {
ASSERT_STRING("AddProductEvent: Could not write the new event value");
return false;
}
return true;
}
bool RlzValueStoreRegistry::ReadProductEvents(Product product,
std::vector<std::string>* events) {
// Open the events key.
base::win::RegKey events_key;
GetEventsRegKey(kEventsSubkeyName, &product, KEY_READ, &events_key);
if (!events_key.Valid())
return false;
// Append the events to the buffer.
int num_values = 0;
LONG result = ERROR_SUCCESS;
for (num_values = 0; result == ERROR_SUCCESS; ++num_values) {
// Max 32767 bytes according to MSDN, but we never use that much.
const size_t kMaxValueNameLength = 2048;
char buffer[kMaxValueNameLength];
DWORD size = arraysize(buffer);
result = RegEnumValueA(events_key.Handle(), num_values, buffer, &size,
NULL, NULL, NULL, NULL);
if (result == ERROR_SUCCESS)
events->push_back(std::string(buffer));
}
return result == ERROR_NO_MORE_ITEMS;
}
bool RlzValueStoreRegistry::ClearProductEvent(Product product,
const char* event_rlz) {
std::wstring event_rlz_wide(ASCIIToWide(event_rlz));
base::win::RegKey key;
GetEventsRegKey(kEventsSubkeyName, &product, KEY_WRITE, &key);
key.DeleteValue(event_rlz_wide.c_str());
// Verify deletion.
DWORD value;
if (key.ReadValueDW(event_rlz_wide.c_str(), &value) == ERROR_SUCCESS) {
ASSERT_STRING("ClearProductEvent: Could not delete the event value.");
return false;
}
return true;
}
bool RlzValueStoreRegistry::ClearAllProductEvents(Product product) {
return ClearAllProductEventValues(product, kEventsSubkeyName);
}
bool RlzValueStoreRegistry::AddStatefulEvent(Product product,
const char* event_rlz) {
base::win::RegKey key;
std::wstring event_rlz_wide(ASCIIToWide(event_rlz));
if (!GetEventsRegKey(kStatefulEventsSubkeyName, &product, KEY_WRITE, &key) ||
key.WriteValue(event_rlz_wide.c_str(), 1) != ERROR_SUCCESS) {
ASSERT_STRING(
"AddStatefulEvent: Could not write the new stateful event");
return false;
}
return true;
}
bool RlzValueStoreRegistry::IsStatefulEvent(Product product,
const char* event_rlz) {
DWORD value;
base::win::RegKey key;
GetEventsRegKey(kStatefulEventsSubkeyName, &product, KEY_READ, &key);
std::wstring event_rlz_wide(ASCIIToWide(event_rlz));
return key.ReadValueDW(event_rlz_wide.c_str(), &value) == ERROR_SUCCESS;
}
bool RlzValueStoreRegistry::ClearAllStatefulEvents(Product product) {
return ClearAllProductEventValues(product, kStatefulEventsSubkeyName);
}
void RlzValueStoreRegistry::CollectGarbage() {
// Delete each of the known subkeys if empty.
const char* subkeys[] = {
kRlzsSubkeyName,
kEventsSubkeyName,
kStatefulEventsSubkeyName,
kPingTimesSubkeyName
};
for (int i = 0; i < arraysize(subkeys); i++) {
std::string subkey_name;
base::StringAppendF(&subkey_name, "%s\\%s", kLibKeyName, subkeys[i]);
AppendBrandToString(&subkey_name);
VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER,
ASCIIToWide(subkey_name).c_str()));
}
// Delete the library key and its parents too now if empty.
VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER, GetWideLibKeyName().c_str()));
VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER, kGoogleCommonKeyName));
VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER, kGoogleKeyName));
}
ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() {
if (!lock_.failed())
store_.reset(new RlzValueStoreRegistry);
}
ScopedRlzValueStoreLock::~ScopedRlzValueStoreLock() {
}
RlzValueStore* ScopedRlzValueStoreLock::GetStore() {
return store_.get();
}
} // namespace rlz_lib

@ -0,0 +1,55 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef RLZ_WIN_LIB_RLZ_VALUE_STORE_REGISTRY_H_
#define RLZ_WIN_LIB_RLZ_VALUE_STORE_REGISTRY_H_
#include "base/compiler_specific.h"
#include "rlz/lib/rlz_value_store.h"
namespace rlz_lib {
// Implements RlzValueStore by storing values in the windows registry.
class RlzValueStoreRegistry : public RlzValueStore {
public:
static std::wstring GetWideLibKeyName();
virtual bool HasAccess(AccessType type) OVERRIDE;
virtual bool WritePingTime(Product product, int64 time) OVERRIDE;
virtual bool ReadPingTime(Product product, int64* time) OVERRIDE;
virtual bool ClearPingTime(Product product) OVERRIDE;
virtual bool WriteAccessPointRlz(AccessPoint access_point,
const char* new_rlz) OVERRIDE;
virtual bool ReadAccessPointRlz(AccessPoint access_point,
char* rlz,
size_t rlz_size) OVERRIDE;
virtual bool ClearAccessPointRlz(AccessPoint access_point) OVERRIDE;
virtual bool AddProductEvent(Product product, const char* event_rlz) OVERRIDE;
virtual bool ReadProductEvents(Product product,
std::vector<std::string>* events) OVERRIDE;
virtual bool ClearProductEvent(Product product,
const char* event_rlz) OVERRIDE;
virtual bool ClearAllProductEvents(Product product) OVERRIDE;
virtual bool AddStatefulEvent(Product product,
const char* event_rlz) OVERRIDE;
virtual bool IsStatefulEvent(Product product,
const char* event_rlz) OVERRIDE;
virtual bool ClearAllStatefulEvents(Product product) OVERRIDE;
virtual void CollectGarbage() OVERRIDE;
private:
RlzValueStoreRegistry() {}
DISALLOW_COPY_AND_ASSIGN(RlzValueStoreRegistry);
friend class ScopedRlzValueStoreLock;
};
} // namespace rlz_lib
#endif // RLZ_WIN_LIB_RLZ_VALUE_STORE_REGISTRY_H_

99
rlz/win/lib/vista_winnt.h Normal file

@ -0,0 +1,99 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// This file contains snippets borrowed from the Vista SDK version of
// WinNT.h, (c) Microsoft (2006)
#ifndef RLZ_WIN_LIB_VISTA_WINNT_H_
#define RLZ_WIN_LIB_VISTA_WINNT_H_
#include <windows.h>
// If no Vista SDK yet, borrow these from Vista's version of WinNT.h
#ifndef SE_GROUP_INTEGRITY
// TOKEN_MANDATORY_LABEL.Label.Attributes = SE_GROUP_INTEGRITY
#define SE_GROUP_INTEGRITY (0x00000020L)
#define SE_GROUP_INTEGRITY_ENABLED (0x00000040L)
typedef struct _TOKEN_MANDATORY_LABEL {
SID_AND_ATTRIBUTES Label;
} TOKEN_MANDATORY_LABEL, *PTOKEN_MANDATORY_LABEL;
// These are a few new enums for TOKEN_INFORMATION_CLASS
#define TokenElevationType static_cast<TOKEN_INFORMATION_CLASS>(18)
#define TokenLinkedToken static_cast<TOKEN_INFORMATION_CLASS>(19)
#define TokenElevation static_cast<TOKEN_INFORMATION_CLASS>(20)
#define TokenHasRestrictions static_cast<TOKEN_INFORMATION_CLASS>(21)
#define TokenAccessInformation static_cast<TOKEN_INFORMATION_CLASS>(22)
#define TokenVirtualizationAllowed static_cast<TOKEN_INFORMATION_CLASS>(23)
#define TokenVirtualizationEnabled static_cast<TOKEN_INFORMATION_CLASS>(24)
// TokenIntegrityLevel is the proces's privilege level, low, med, or high
#define TokenIntegrityLevel static_cast<TOKEN_INFORMATION_CLASS>(25)
// TokenIntegrityLevelDeasktop is an alternate level used for access apis
// (screen readers, imes)
#define TokenIntegrityLevelDesktop static_cast<TOKEN_INFORMATION_CLASS>(26)
// This is a new flag to pass to GetNamedSecurityInfo or SetNamedSecurityInfo
// that puts the mandatory level label info in an access control list (ACL)
// structure in the parameter normally used for system acls (SACL)
#define LABEL_SECURITY_INFORMATION (0x00000010L)
// The new Access Control Entry type identifier for mandatory labels
#define SYSTEM_MANDATORY_LABEL_ACE_TYPE (0x11)
// The structure of mandatory label acess control binary entry
typedef struct _SYSTEM_MANDATORY_LABEL_ACE {
ACE_HEADER Header;
ACCESS_MASK Mask;
DWORD SidStart;
} SYSTEM_MANDATORY_LABEL_ACE, *PSYSTEM_MANDATORY_LABEL_ACE;
// Masks for ACCESS_MASK above
#define SYSTEM_MANDATORY_LABEL_NO_WRITE_UP 0x1
#define SYSTEM_MANDATORY_LABEL_NO_READ_UP 0x2
#define SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP 0x4
#define SYSTEM_MANDATORY_LABEL_VALID_MASK \
(SYSTEM_MANDATORY_LABEL_NO_WRITE_UP | \
SYSTEM_MANDATORY_LABEL_NO_READ_UP | \
SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP)
// The SID authority for mandatory labels
#define SECURITY_MANDATORY_LABEL_AUTHORITY {0, 0, 0, 0, 0, 16}
// the RID values (sub authorities) that define mandatory label levels
#define SECURITY_MANDATORY_UNTRUSTED_RID (0x00000000L)
#define SECURITY_MANDATORY_LOW_RID (0x00001000L)
#define SECURITY_MANDATORY_MEDIUM_RID (0x00002000L)
#define SECURITY_MANDATORY_HIGH_RID (0x00003000L)
#define SECURITY_MANDATORY_SYSTEM_RID (0x00004000L)
#define SECURITY_MANDATORY_UI_ACCESS_RID (0x00004100L)
#define SECURITY_MANDATORY_PROTECTED_PROCESS_RID (0x00005000L)
// Vista's mandatory labels, enumerated
typedef enum _MANDATORY_LEVEL {
MandatoryLevelUntrusted = 0,
MandatoryLevelLow,
MandatoryLevelMedium,
MandatoryLevelHigh,
MandatoryLevelSystem,
MandatoryLevelSecureProcess,
MandatoryLevelCount
} MANDATORY_LEVEL, *PMANDATORY_LEVEL;
// Token elevation values describe the relative strength of a given token.
// A full token is a token with all groups and privileges to which the
// principal is authorized. A limited token is one with some groups or
// privileges removed.
typedef enum _TOKEN_ELEVATION_TYPE {
TokenElevationTypeDefault = 1,
TokenElevationTypeFull,
TokenElevationTypeLimited,
} TOKEN_ELEVATION_TYPE, *PTOKEN_ELEVATION_TYPE;
#endif // #ifndef SE_GROUP_INTEGRITY
#endif // RLZ_WIN_LIB_VISTA_WINNT_H_