summaryrefslogtreecommitdiffstats
path: root/chrome/browser/sync/util
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/sync/util')
-rw-r--r--chrome/browser/sync/util/character_set_converters-linux.cc60
-rw-r--r--chrome/browser/sync/util/character_set_converters-win32.cc62
-rw-r--r--chrome/browser/sync/util/character_set_converters.cc54
-rw-r--r--chrome/browser/sync/util/character_set_converters.h236
-rw-r--r--chrome/browser/sync/util/character_set_converters_unittest.cc168
-rw-r--r--chrome/browser/sync/util/closure.h12
-rw-r--r--chrome/browser/sync/util/compat-file-posix.cc12
-rw-r--r--chrome/browser/sync/util/compat-file-win.cc14
-rw-r--r--chrome/browser/sync/util/compat-file.h31
-rw-r--r--chrome/browser/sync/util/compat-pthread.h38
-rw-r--r--chrome/browser/sync/util/crypto_helpers.cc62
-rw-r--r--chrome/browser/sync/util/crypto_helpers.h40
-rw-r--r--chrome/browser/sync/util/crypto_helpers_unittest.cc17
-rw-r--r--chrome/browser/sync/util/data_encryption.cc51
-rw-r--r--chrome/browser/sync/util/data_encryption.h21
-rw-r--r--chrome/browser/sync/util/data_encryption_unittest.cc31
-rw-r--r--chrome/browser/sync/util/dbgq.h27
-rw-r--r--chrome/browser/sync/util/event_sys-inl.h340
-rw-r--r--chrome/browser/sync/util/event_sys.h41
-rw-r--r--chrome/browser/sync/util/event_sys_unittest.cc271
-rw-r--r--chrome/browser/sync/util/fast_dump.h60
-rw-r--r--chrome/browser/sync/util/highres_timer-linux.cc29
-rw-r--r--chrome/browser/sync/util/highres_timer-linux.h79
-rw-r--r--chrome/browser/sync/util/highres_timer-win32.cc46
-rw-r--r--chrome/browser/sync/util/highres_timer-win32.h78
-rw-r--r--chrome/browser/sync/util/highres_timer.h13
-rw-r--r--chrome/browser/sync/util/highres_timer_unittest.cc49
-rw-r--r--chrome/browser/sync/util/path_helpers-linux.cc51
-rw-r--r--chrome/browser/sync/util/path_helpers-posix.cc99
-rw-r--r--chrome/browser/sync/util/path_helpers.cc153
-rw-r--r--chrome/browser/sync/util/path_helpers.h105
-rw-r--r--chrome/browser/sync/util/path_helpers_unittest.cc131
-rw-r--r--chrome/browser/sync/util/pthread_helpers.cc162
-rw-r--r--chrome/browser/sync/util/pthread_helpers.h259
-rw-r--r--chrome/browser/sync/util/pthread_helpers_fwd.h13
-rw-r--r--chrome/browser/sync/util/query_helpers.cc282
-rw-r--r--chrome/browser/sync/util/query_helpers.h698
-rw-r--r--chrome/browser/sync/util/query_helpers_unittest.cc36
-rw-r--r--chrome/browser/sync/util/row_iterator.h122
-rw-r--r--chrome/browser/sync/util/signin.h15
-rw-r--r--chrome/browser/sync/util/sync_types.h75
-rw-r--r--chrome/browser/sync/util/user_settings-posix.cc34
-rw-r--r--chrome/browser/sync/util/user_settings-win32.cc67
-rw-r--r--chrome/browser/sync/util/user_settings.cc350
-rw-r--r--chrome/browser/sync/util/user_settings.h114
-rw-r--r--chrome/browser/sync/util/user_settings_unittest.cc86
46 files changed, 4794 insertions, 0 deletions
diff --git a/chrome/browser/sync/util/character_set_converters-linux.cc b/chrome/browser/sync/util/character_set_converters-linux.cc
new file mode 100644
index 0000000..96ad1b6
--- /dev/null
+++ b/chrome/browser/sync/util/character_set_converters-linux.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2009 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 "chrome/browser/sync/util/character_set_converters.h"
+
+#include <string>
+
+using std::string;
+
+namespace browser_sync {
+
+// Converts input_string to UTF8 and appends the result into output_string.
+void AppendPathStringToUTF8(const PathChar *wide, int size,
+ string* output_string) {
+ output_string->append(wide, size);
+}
+
+bool AppendUTF8ToPathString(const char* utf8, size_t size,
+ PathString* output_string) {
+ output_string->append(utf8, size);
+ return true;
+}
+
+void TrimPathStringToValidCharacter(PathString* string) {
+ // Constants from http://en.wikipedia.org/wiki/UTF-8
+ CHECK(string);
+ if (string->empty())
+ return;
+ if (0 == (string->at(string->length() - 1) & 0x080))
+ return;
+ int partial_enc_bytes = 0;
+ for (partial_enc_bytes = 0 ; true ; ++partial_enc_bytes) {
+ if (4 == partial_enc_bytes || partial_enc_bytes == string->length()) {
+ // original string was broken, garbage in, garbage out.
+ return;
+ }
+ PathChar c = string->at(string->length() - 1 - partial_enc_bytes);
+ if ((c & 0x0c0) == 0x080) // utf continuation char;
+ continue;
+ if ((c & 0x0e0) == 0x0e0) // 2-byte encoded char.
+ if (1 == partial_enc_bytes)
+ return;
+ else
+ break;
+ if ((c & 0x0f0) == 0xc0) // 3-byte encoded char.
+ if (2 == partial_enc_bytes)
+ return;
+ else
+ break;
+ if ((c & 0x0f8) == 0x0f0) // 4-byte encoded char.
+ if (3 == partial_enc_bytes)
+ return;
+ else
+ break;
+ }
+ string->resize(string->length() - 1 - partial_enc_bytes);
+}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/util/character_set_converters-win32.cc b/chrome/browser/sync/util/character_set_converters-win32.cc
new file mode 100644
index 0000000..79e9281
--- /dev/null
+++ b/chrome/browser/sync/util/character_set_converters-win32.cc
@@ -0,0 +1,62 @@
+// Copyright (c) 2009 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 "chrome/browser/sync/util/character_set_converters.h"
+
+#include <windows.h>
+
+#include <string>
+
+using std::string;
+
+namespace browser_sync {
+
+// Converts input_string to UTF8 and appends the result into to output_string
+void AppendPathStringToUTF8(const PathChar* wide, int size,
+ string* output_string) {
+ CHECK(output_string);
+ if (0 == size)
+ return;
+
+ int needed_space = ::WideCharToMultiByte(CP_UTF8, 0, wide, size, 0, 0, 0, 0);
+ // TODO(sync): This should flag an error when we move to an api that can let
+ // utf-16 -> utf-8 fail.
+ CHECK(0 != needed_space);
+ string::size_type current_size = output_string->size();
+ output_string->resize(current_size + needed_space);
+ CHECK(0 != ::WideCharToMultiByte(CP_UTF8, 0, wide, size,
+ &(*output_string)[current_size], needed_space, 0, 0));
+}
+
+bool AppendUTF8ToPathString(const char* utf8, size_t size,
+ PathString* output_string) {
+ CHECK(output_string);
+ if (0 == size)
+ return true;
+ // TODO(sync): Do we want to force precomposed characters here?
+ int needed_wide_chars = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
+ utf8, size, 0, 0);
+ if (0 == needed_wide_chars) {
+ DWORD err = ::GetLastError();
+ if (MB_ERR_INVALID_CHARS == err)
+ return false;
+ CHECK(0 == err);
+ }
+ PathString::size_type current_length = output_string->size();
+ output_string->resize(current_length + needed_wide_chars);
+ CHECK(0 != ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8, size,
+ &(*output_string)[current_length], needed_wide_chars));
+ return true;
+}
+
+void TrimPathStringToValidCharacter(PathString* string) {
+ CHECK(string);
+ // Constants from http://en.wikipedia.org/wiki/UTF-16
+ if (string->empty())
+ return;
+ if (0x0dc00 == (string->at(string->length() - 1) & 0x0fc00))
+ string->resize(string->length() - 1);
+}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/util/character_set_converters.cc b/chrome/browser/sync/util/character_set_converters.cc
new file mode 100644
index 0000000..a9114cf3
--- /dev/null
+++ b/chrome/browser/sync/util/character_set_converters.cc
@@ -0,0 +1,54 @@
+// Copyright (c) 2009 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 "chrome/browser/sync/util/character_set_converters.h"
+
+#include <string>
+
+using std::string;
+
+namespace browser_sync {
+
+void PathStringToUTF8(const PathChar* wide, int size,
+ std::string* output_string) {
+ CHECK(output_string);
+ output_string->clear();
+ AppendPathStringToUTF8(wide, size, output_string);
+}
+
+bool UTF8ToPathString(const char* utf8, size_t size,
+ PathString* output_string) {
+ CHECK(output_string);
+ output_string->clear();
+ return AppendUTF8ToPathString(utf8, size, output_string);
+};
+
+ToUTF8::ToUTF8(const PathChar* wide, size_t size) {
+ PathStringToUTF8(wide, size, &result_);
+}
+
+ToUTF8::ToUTF8(const PathString& wide) {
+ PathStringToUTF8(wide.data(), wide.length(), &result_);
+}
+
+ToUTF8::ToUTF8(const PathChar* wide) {
+ PathStringToUTF8(wide, PathLen(wide), &result_);
+}
+
+ToPathString::ToPathString(const char* utf8, size_t size) {
+ good_ = UTF8ToPathString(utf8, size, &result_);
+ good_checked_ = false;
+}
+
+ToPathString::ToPathString(const std::string& utf8) {
+ good_ = UTF8ToPathString(utf8.data(), utf8.length(), &result_);
+ good_checked_ = false;
+}
+
+ToPathString::ToPathString(const char* utf8) {
+ good_ = UTF8ToPathString(utf8, strlen(utf8), &result_);
+ good_checked_ = false;
+}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/util/character_set_converters.h b/chrome/browser/sync/util/character_set_converters.h
new file mode 100644
index 0000000..3e614c3
--- /dev/null
+++ b/chrome/browser/sync/util/character_set_converters.h
@@ -0,0 +1,236 @@
+// Copyright (c) 2009 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 CHROME_BROWSER_SYNC_UTIL_CHARACTER_SET_CONVERTERS_H_
+#define CHROME_BROWSER_SYNC_UTIL_CHARACTER_SET_CONVERTERS_H_
+
+// A pair of classes to convert UTF8 <-> UCS2 character strings.
+//
+// Note that the current implementation is limited to UCS2, whereas the
+// interface is agnostic to the wide encoding used.
+//
+// Also note that UCS2 is different from UTF-16, in that UTF-16 can encode all
+// the code points in the Unicode character set by multi-character encodings,
+// while UCS2 is limited to encoding < 2^16 code points.
+//
+// It appears that Windows support UTF-16, which means we have to be careful
+// what we feed this class.
+//
+// Usage:
+// string utf8;
+// CHECK(browser_sync::Append(wide_string, &utf8));
+// PathString bob;
+// CHECK(browser_sync::Append(utf8, &bob));
+// PathString fred = bob;
+
+#ifdef OS_LINUX
+#include <glib.h>
+#endif
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/string16.h"
+#include "chrome/browser/sync/util/sync_types.h"
+
+// Need to cast literals (Linux, OSX)
+#define STRING16_UGLY_DOUBLE_DEFINE_HACK(s) \
+ reinterpret_cast<const char16*>(L##s)
+#define STRING16(s) STRING16_UGLY_DOUBLE_DEFINE_HACK(s)
+
+using std::string;
+
+namespace browser_sync {
+
+// These 2 classes are deprecated. Instead, prefer the Append() functions.
+
+// A class to convert wide -> UTF8.
+class ToUTF8 {
+ public:
+ explicit ToUTF8(const PathChar* wide);
+ ToUTF8(const PathChar* wide, PathString::size_type size);
+ explicit ToUTF8(const PathString& wide);
+
+ // cast operators
+ operator const std::string&() const;
+ operator const char*() const;
+
+ // accessors
+ const std::string& get_string() const;
+ const char* data() const;
+ std::string::size_type byte_length() const;
+
+ private:
+ std::string result_;
+};
+
+// A class to convert UTF8 -> wide.
+class ToPathString {
+ public:
+ explicit ToPathString(const char*);
+ ToPathString(const char*, size_t size);
+ explicit ToPathString(const std::string&);
+
+ // true iff UTF-8 to wide conversion succeeded in constructor.
+ bool good() {
+ good_checked_ = true;
+ return good_;
+ }
+
+ // It's invalid to invoke the accessors or the cast operators unless the
+ // string is good and good() has been invoked at least once.
+
+ // Implicit casts to const PathString& and const PathChar*
+ operator const PathString&() const;
+ operator const PathChar*() const;
+
+ // Accessors
+ const PathString& get_string16() const;
+ const PathChar* data() const;
+ PathString::size_type length() const;
+
+ private:
+ PathString result_;
+
+ // Conversion succeeded.
+ bool good_;
+ // good() has been invoked at least once.
+ bool good_checked_;
+};
+
+// Converts the UCS2 string "wide" to UTF8 encoding and stores the result in
+// output_string.
+void PathStringToUTF8(const PathChar* wide, int size,
+ std::string* output_string);
+
+// Converts UCS2 string wide to UTF8 encoding and appends the result to
+// output_string.
+void AppendPathStringToUTF8(const PathChar* wide, int size,
+ std::string* output_string);
+
+// Converts the UTF8 encoded string "utf8" to UCS16 and stores the result in
+// output_string.
+//
+// Returns true iff conversion was successful, false otherwise.
+bool UTF8ToPathString(const char* utf8, size_t size,
+ PathString* output_string);
+
+// Converts the UTF8 encoded string "utf8" to UCS2 and appends the result in
+// output_string.
+//
+// Returns true iff conversion was successful, false otherwise.
+bool AppendUTF8ToPathString(const char* utf8, size_t size,
+ PathString* output_string);
+
+// Converts the UTF8 encoded string "utf8" to UCS2 and appends the result in
+// output_string.
+//
+// @returns true iff conversion was successful, false otherwise.
+inline bool AppendUTF8ToPathString(const std::string& utf8,
+ PathString* output_string) {
+ return AppendUTF8ToPathString(utf8.data(), utf8.length(), output_string);
+}
+
+// Converts UCS2 string wide to UTF8 encoding and appends the result to
+// output_string.
+inline void AppendPathStringToUTF8(const PathString& wide,
+ std::string* output_string) {
+ return AppendPathStringToUTF8(wide.data(), wide.length(), output_string);
+}
+
+
+inline bool Append(const PathChar* wide, int size,
+ std::string* output_string) {
+ AppendPathStringToUTF8(wide, size, output_string);
+ return true;
+}
+
+inline bool Append(const PathChar* wide, std::string* output_string) {
+ AppendPathStringToUTF8(wide, PathLen(wide), output_string);
+ return true;
+}
+
+inline bool Append(const std::string& utf8, PathString* output_string) {
+ return AppendUTF8ToPathString(utf8.data(), utf8.length(), output_string);
+}
+
+#if !PATHSTRING_IS_STD_STRING
+inline bool Append(const char* utf8, size_t size, PathString* output_string) {
+ return AppendUTF8ToPathString(utf8, size, output_string);
+}
+
+inline bool Append(const char* s, int size, std::string* output_string) {
+ output_string->append(s, size);
+ return true;
+}
+
+inline bool Append(const char* utf8, PathString* output_string) {
+ return AppendUTF8ToPathString(utf8, strlen(utf8), output_string);
+}
+
+inline bool Append(const char* s, std::string* output_string) {
+ output_string->append(s);
+ return true;
+}
+
+inline bool Append(const PathString& wide, std::string* output_string) {
+ return Append(wide.data(), wide.length(), output_string);
+}
+
+inline bool Append(const std::string& s, std::string* output_string) {
+ return Append(s.data(), s.length(), output_string);
+}
+#endif
+
+inline ToUTF8::operator const std::string&() const {
+ return result_;
+}
+
+inline ToUTF8::operator const char*() const {
+ return result_.c_str();
+}
+
+inline const std::string& ToUTF8::get_string() const {
+ return result_;
+}
+
+inline const char* ToUTF8::data() const {
+ return result_.data();
+}
+
+inline std::string::size_type ToUTF8::byte_length() const {
+ return result_.size();
+}
+
+inline ToPathString::operator const PathString&() const {
+ DCHECK(good_ && good_checked_);
+ return result_;
+}
+
+inline ToPathString::operator const PathChar*() const {
+ DCHECK(good_ && good_checked_);
+ return result_.c_str();
+}
+
+inline const PathString& ToPathString::get_string16() const {
+ DCHECK(good_ && good_checked_);
+ return result_;
+}
+
+inline const PathChar* ToPathString::data() const {
+ DCHECK(good_ && good_checked_);
+ return result_.data();
+}
+
+inline PathString::size_type ToPathString::length() const {
+ DCHECK(good_ && good_checked_);
+ return result_.length();
+}
+
+void TrimPathStringToValidCharacter(PathString* string);
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_UTIL_CHARACTER_SET_CONVERTERS_H_
diff --git a/chrome/browser/sync/util/character_set_converters_unittest.cc b/chrome/browser/sync/util/character_set_converters_unittest.cc
new file mode 100644
index 0000000..838bbd1
--- /dev/null
+++ b/chrome/browser/sync/util/character_set_converters_unittest.cc
@@ -0,0 +1,168 @@
+// Copyright (c) 2009 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 "chrome/browser/sync/util/character_set_converters.h"
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using browser_sync::ToPathString;
+using browser_sync::ToUTF8;
+using browser_sync::AppendPathStringToUTF8;
+using browser_sync::AppendUTF8ToPathString;
+using browser_sync::PathStringToUTF8;
+using browser_sync::UTF8ToPathString;
+using std::string;
+
+class CharacterSetConverterTest : public testing::Test {
+};
+
+TEST_F(CharacterSetConverterTest, ASCIIConversionTest) {
+ string ascii = "Test String";
+ PathString wide = PSTR("Test String");
+ ToPathString to_wide(ascii);
+ ASSERT_TRUE(to_wide.good());
+ ToUTF8 to_utf8(wide);
+
+ // Using == as gunit doesn't handle PathString equality tests correctly (it
+ // tries to print the PathString and fails).
+ ASSERT_TRUE(PathString(wide) == to_wide.get_string16());
+ ASSERT_EQ(string(ascii), to_utf8.get_string());
+ ToPathString to_16(ascii);
+ ASSERT_TRUE(to_16.good());
+ ASSERT_TRUE(PathString(wide) == to_16.get_string16());
+#ifdef OS_WINDOWS
+ // On Linux, PathString is already UTF8
+ ASSERT_EQ(string(ascii), static_cast<string>(ToUTF8(wide)));
+#endif
+ // The next line fails the good_checked_ test. It would be a good death test
+ // but they don't work on Windows.
+ // ASSERT_TRUE(wide == ToPathString(utf8).get_string16());
+}
+
+#ifdef OS_WINDOWS
+ // On Linux, PathString is already UTF8
+TEST_F(CharacterSetConverterTest, UnicodeConversionText) {
+ // Source data obtained by running od -b on files saved in utf-8 and unicode
+ // from a text editor.
+ const char* utf8 = "\357\273\277\150\145\154\154\157\040\303\250\303\251"
+ "\302\251\342\202\254\302\243\302\245\302\256\342\204\242";
+// #ifdef IS_LITTLE_ENDIAN
+ const PathChar* wide = reinterpret_cast<const PathChar*>("\377\376\150\000"
+ "\145\000\154\000\154\000\157\000\040\000\350\000\351\000\251\000\254\040"
+ "\243\000\245\000\256\000\042\041");
+// #else
+// // This should work, but on Windows we don't have the endian
+// // macros. Since we only do conversion between 16<->8 on Windows,
+// // it's safe to assume little endian.
+// const PathChar* wide =
+// reinterpret_cast<PathChar*>("\376\377\000\150\000\145\000"
+// "\154\000\154\000\157\000\040\000\350\000\351\000\251\040\254\000\243"
+// "\000\245\000\256\041\042");
+// #endif
+
+ ToPathString to_wide(utf8);
+ ASSERT_TRUE(to_wide.good());
+ ToUTF8 to_utf8(wide);
+
+ // Using == as gunit doesn't handle PathString equality tests correctly (it
+ // tries to print the PathString and fails).
+ ASSERT_TRUE(wide == to_wide.get_string16());
+ ASSERT_EQ(string(utf8), to_utf8.get_string());
+ ToPathString to_16(utf8);
+ ASSERT_TRUE(to_16.good());
+ ASSERT_TRUE(wide == to_16.get_string16());
+ ASSERT_EQ(string(utf8), reinterpret_cast<const string&>(ToUTF8(wide)));
+}
+#endif
+
+TEST_F(CharacterSetConverterTest, AppendUTF8Tests) {
+ PathString one = PSTR("one");
+ PathString two = PSTR("two");
+ PathString three = PSTR("three");
+ string out;
+ AppendPathStringToUTF8(one.data(), one.length(), &out);
+ AppendPathStringToUTF8(two.data(), two.length(), &out);
+ AppendPathStringToUTF8(three.data(), three.length(), &out);
+ ASSERT_EQ(out, "onetwothree");
+ PathString onetwothree = PSTR("onetwothree");
+ PathStringToUTF8(onetwothree.data(), onetwothree.length(), &out);
+ ASSERT_EQ(out, "onetwothree");
+}
+
+TEST_F(CharacterSetConverterTest, AppendPathStringTests) {
+ string one = "one";
+ string two = "two";
+ string three = "three";
+ PathString out;
+ AppendUTF8ToPathString(one.data(), one.length(), &out);
+ AppendUTF8ToPathString(two.data(), two.length(), &out);
+ AppendUTF8ToPathString(three.data(), three.length(), &out);
+ ASSERT_TRUE(out == PathString(PSTR("onetwothree")));
+ string onetwothree = "onetwothree";
+ UTF8ToPathString(onetwothree.data(), onetwothree.length(), &out);
+ ASSERT_TRUE(out == PathString(PSTR("onetwothree")));
+}
+
+#ifdef OS_WINDOWS
+namespace {
+// See http://en.wikipedia.org/wiki/UTF-16 for an explanation of UTF16.
+// For a test case we use the UTF-8 and UTF-16 encoding of char 119070
+// (hex 1D11E), which is musical G clef.
+const unsigned char utf8_test_string[] = {
+ 0xEF, 0xBB, 0xBF, // BOM
+ 0xE6, 0xB0, 0xB4, // water, Chinese (0x6C34)
+ 0x7A, // lower case z
+ 0xF0, 0x9D, 0x84, 0x9E, // musical G clef (0x1D11E)
+ 0x00,
+};
+const PathChar utf16_test_string[] = {
+ 0xFEFF, // BOM
+ 0x6C34, // water, Chinese
+ 0x007A, // lower case z
+ 0xD834, 0xDD1E, // musical G clef (0x1D11E)
+ 0x0000,
+};
+}
+
+TEST_F(CharacterSetConverterTest, UTF16ToUTF8Test) {
+ // Avoid truncation warning.
+ const char* utf8_test_string_pointer =
+ reinterpret_cast<const char*>(utf8_test_string);
+ ASSERT_STREQ(utf8_test_string_pointer, ToUTF8(utf16_test_string));
+}
+
+TEST_F(CharacterSetConverterTest, utf8_test_stringToUTF16Test) {
+ // Avoid truncation warning.
+ const char* utf8_test_string_pointer =
+ reinterpret_cast<const char*>(utf8_test_string);
+ ToPathString converted_utf8(utf8_test_string_pointer);
+ ASSERT_TRUE(converted_utf8.good());
+ ASSERT_EQ(wcscmp(utf16_test_string, converted_utf8), 0);
+}
+
+TEST(NameTruncation, WindowsNameTruncation) {
+ using browser_sync::TrimPathStringToValidCharacter;
+ PathChar array[] = {'1', '2', 0xD950, 0xDF21, '3', '4', 0};
+ PathString message = array;
+ ASSERT_EQ(message.length(), arraysize(array) - 1);
+ int old_length = message.length();
+ while (old_length != 0) {
+ TrimPathStringToValidCharacter(&message);
+ if (old_length == 4)
+ EXPECT_EQ(3, message.length());
+ else
+ EXPECT_EQ(old_length, message.length());
+ message.resize(message.length() - 1);
+ old_length = message.length();
+ }
+ TrimPathStringToValidCharacter(&message);
+}
+#else
+
+// TODO(zork): Add unittests here once we're running these tests on linux.
+
+#endif
diff --git a/chrome/browser/sync/util/closure.h b/chrome/browser/sync/util/closure.h
new file mode 100644
index 0000000..b282bc4
--- /dev/null
+++ b/chrome/browser/sync/util/closure.h
@@ -0,0 +1,12 @@
+// Copyright (c) 2009 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 CHROME_BROWSER_SYNC_UTIL_CLOSURE_H_
+#define CHROME_BROWSER_SYNC_UTIL_CLOSURE_H_
+
+#include "base/task.h"
+
+typedef CallbackRunner<Tuple0> Closure;
+
+#endif // CHROME_BROWSER_SYNC_UTIL_CLOSURE_H_
diff --git a/chrome/browser/sync/util/compat-file-posix.cc b/chrome/browser/sync/util/compat-file-posix.cc
new file mode 100644
index 0000000..66582fa
--- /dev/null
+++ b/chrome/browser/sync/util/compat-file-posix.cc
@@ -0,0 +1,12 @@
+// Copyright (c) 2009 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.
+
+#if ((!defined(OS_LINUX)) && (!defined(OS_MACOSX)))
+#error Compile this file on Mac OS X or Linux only.
+#endif
+
+#include "chrome/browser/sync/util/compat-file.h"
+
+const char* const kPathSeparator = "/";
+
diff --git a/chrome/browser/sync/util/compat-file-win.cc b/chrome/browser/sync/util/compat-file-win.cc
new file mode 100644
index 0000000..d812d68
--- /dev/null
+++ b/chrome/browser/sync/util/compat-file-win.cc
@@ -0,0 +1,14 @@
+// Copyright (c) 2009 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 OS_WINDOWS
+#error Compile this file on Windows only.
+#endif
+
+#include "chrome/browser/sync/util/compat-file.h"
+
+#include <windows.h>
+
+const wchar_t* const kPathSeparator = L"\\";
+
diff --git a/chrome/browser/sync/util/compat-file.h b/chrome/browser/sync/util/compat-file.h
new file mode 100644
index 0000000..273e3cb
--- /dev/null
+++ b/chrome/browser/sync/util/compat-file.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2009 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.
+//
+// File compatability routines. Useful to delete database files with.
+//
+#ifndef CHROME_BROWSER_SYNC_UTIL_COMPAT_FILE_H_
+#define CHROME_BROWSER_SYNC_UTIL_COMPAT_FILE_H_
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "chrome/browser/sync/util/sync_types.h"
+
+extern const PathChar* const kPathSeparator;
+
+// Path calls for all OSes.
+// Returns 0 on success, non-zero on failure.
+int PathRemove(const PathString& path);
+
+#ifdef OS_WINDOWS
+inline int PathRemove(const PathString& path) {
+ return _wremove(path.c_str());
+}
+#elif (defined(OS_MACOSX) || defined(OS_LINUX))
+inline int PathRemove(const PathString& path) {
+ return unlink(path.c_str());
+}
+#endif
+
+#endif // CHROME_BROWSER_SYNC_UTIL_COMPAT_FILE_H_
diff --git a/chrome/browser/sync/util/compat-pthread.h b/chrome/browser/sync/util/compat-pthread.h
new file mode 100644
index 0000000..e5817af
--- /dev/null
+++ b/chrome/browser/sync/util/compat-pthread.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2009 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.
+//
+// Pthread compatability routines.
+
+#ifndef CHROME_BROWSER_SYNC_UTIL_COMPAT_PTHREAD_H_
+#define CHROME_BROWSER_SYNC_UTIL_COMPAT_PTHREAD_H_
+
+// TODO(timsteele): This file is deprecated. Use PlatformThread.
+#include "base/platform_thread.h"
+
+#define ThreadId PlatformThreadId
+
+#ifndef OS_WINDOWS
+inline ThreadId GetCurrentThreadId() {
+ return PlatformThread::CurrentId();
+}
+#endif // OS_WINDOWS
+
+#if (!defined(OS_WINDOWS) && !defined(OS_MACOSX))
+// TODO(timsteele): What the heck is this?
+inline int sem_post_multiple(sem_t * sem, int number) {
+ int i;
+ int r = 0;
+ for (i = 0; i < number; i++) {
+ r = sem_post(sem);
+ if (r != 0) {
+ LOG_IF(ERROR, i > 0) << "sem_post() failed on iteration #" << i <<
+ " of " << number;
+ return r;
+ }
+ }
+ return 0;
+}
+#endif // (!defined(OS_WINDOWS) && !defined(OS_MACOSX))
+
+#endif // CHROME_BROWSER_SYNC_UTIL_COMPAT_PTHREAD_H_
diff --git a/chrome/browser/sync/util/crypto_helpers.cc b/chrome/browser/sync/util/crypto_helpers.cc
new file mode 100644
index 0000000..c84bfaf
--- /dev/null
+++ b/chrome/browser/sync/util/crypto_helpers.cc
@@ -0,0 +1,62 @@
+// Copyright (c) 2009 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 "chrome/browser/sync/util/crypto_helpers.h"
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/format_macros.h"
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "base/string_util.h"
+
+using std::string;
+using std::vector;
+
+MD5Calculator::MD5Calculator() {
+ MD5Init(&context_);
+}
+
+void MD5Calculator::AddData(const unsigned char* data, int length) {
+ CHECK(bin_digest_.empty());
+ MD5Update(&context_, data, length);
+}
+
+void MD5Calculator::CalcDigest() {
+ if (bin_digest_.empty()) {
+ MD5Digest digest;
+ MD5Final(&digest, &context_);
+ bin_digest_.assign(digest.a, digest.a + arraysize(digest.a));
+ }
+}
+
+vector<uint8> MD5Calculator::GetDigest() {
+ CalcDigest();
+ return bin_digest_;
+}
+
+PathString MD5Calculator::GetHexDigest() {
+ CalcDigest();
+ string hex = HexEncode(reinterpret_cast<char*>(&bin_digest_.front()),
+ bin_digest_.size());
+ StringToLowerASCII(&hex);
+ return PathString(hex.begin(), hex.end());
+}
+
+void GetRandomBytes(char* output, int output_length) {
+ for (int i = 0; i < output_length; i++) {
+ // TODO(chron): replace this with something less stupid.
+ output[i] = static_cast<char>(base::RandUint64());
+ }
+}
+
+string Generate128BitRandomHexString() {
+ int64 chunk1 = static_cast<int64>(base::RandUint64());
+ int64 chunk2 = static_cast<int64>(base::RandUint64());
+
+ return StringPrintf("%016" PRId64 "x%016" PRId64 "x",
+ chunk1, chunk2);
+}
diff --git a/chrome/browser/sync/util/crypto_helpers.h b/chrome/browser/sync/util/crypto_helpers.h
new file mode 100644
index 0000000..c31a278
--- /dev/null
+++ b/chrome/browser/sync/util/crypto_helpers.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2009 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 CHROME_BROWSER_SYNC_UTIL_CRYPTO_HELPERS_H_
+#define CHROME_BROWSER_SYNC_UTIL_CRYPTO_HELPERS_H_
+
+#include <string>
+#include <vector>
+
+// An object to handle calculation of MD5 sums.
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/md5.h"
+#include "base/port.h"
+#include "chrome/browser/sync/util/sync_types.h"
+
+class MD5Calculator {
+ protected:
+ MD5Context context_;
+ std::vector<uint8> bin_digest_;
+
+ void CalcDigest();
+ public:
+ MD5Calculator();
+ ~MD5Calculator() {}
+ void AddData(const uint8* data, int length);
+ void AddData(const char* data, int length) {
+ AddData(reinterpret_cast<const uint8*>(data), length);
+ }
+ PathString GetHexDigest();
+ std::vector<uint8> GetDigest();
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MD5Calculator);
+};
+
+void GetRandomBytes(char* output, int output_length);
+std::string Generate128BitRandomHexString();
+
+#endif // CHROME_BROWSER_SYNC_UTIL_CRYPTO_HELPERS_H_
diff --git a/chrome/browser/sync/util/crypto_helpers_unittest.cc b/chrome/browser/sync/util/crypto_helpers_unittest.cc
new file mode 100644
index 0000000..1d2dd60
--- /dev/null
+++ b/chrome/browser/sync/util/crypto_helpers_unittest.cc
@@ -0,0 +1,17 @@
+// Copyright (c) 2009 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 "chrome/browser/sync/util/crypto_helpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(ChecksumTest, MD5ChecksumTest) {
+ uint8 buffer[256];
+ for (unsigned int i = 0; i < arraysize(buffer); ++i) {
+ buffer[i] = i;
+ }
+ MD5Calculator md5;
+ md5.AddData(buffer, arraysize(buffer));
+ PathString checksum(PSTR("e2c865db4162bed963bfaa9ef6ac18f0"));
+ ASSERT_EQ(checksum, md5.GetHexDigest());
+}
diff --git a/chrome/browser/sync/util/data_encryption.cc b/chrome/browser/sync/util/data_encryption.cc
new file mode 100644
index 0000000..b835147
--- /dev/null
+++ b/chrome/browser/sync/util/data_encryption.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2009 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.
+//
+// NOTE: this file is Winodws specific.
+
+#include "chrome/browser/sync/util/data_encryption.h"
+
+#include <windows.h>
+#include <wincrypt.h>
+
+#include <cstddef>
+#include <string>
+#include <vector>
+
+using std::string;
+using std::vector;
+
+vector<uint8> EncryptData(const string& data) {
+ DATA_BLOB unencrypted_data, encrypted_data;
+ unencrypted_data.pbData = (BYTE*)(data.data());
+ unencrypted_data.cbData = data.size();
+
+ if (!CryptProtectData(&unencrypted_data, L"", NULL, NULL, NULL, 0,
+ &encrypted_data))
+ LOG(ERROR) << "Encryption fails: " << data;
+
+ vector<uint8> result(encrypted_data.pbData,
+ encrypted_data.pbData + encrypted_data.cbData);
+ LocalFree(encrypted_data.pbData);
+ return result;
+}
+
+bool DecryptData(const vector<uint8>& in_data, string* out_data) {
+ DATA_BLOB encrypted_data, decrypted_data;
+ encrypted_data.pbData =
+ (in_data.empty() ? NULL : const_cast<BYTE*>(&in_data[0]));
+ encrypted_data.cbData = in_data.size();
+ LPWSTR descrip = L"";
+
+ if (!CryptUnprotectData(&encrypted_data, &descrip, NULL, NULL, NULL, 0,
+ &decrypted_data)) {
+ LOG(ERROR) << "Decryption fails: ";
+ return false;
+ } else {
+ out_data->assign(reinterpret_cast<const char*>(decrypted_data.pbData),
+ decrypted_data.cbData);
+ LocalFree(decrypted_data.pbData);
+ return true;
+ }
+}
diff --git a/chrome/browser/sync/util/data_encryption.h b/chrome/browser/sync/util/data_encryption.h
new file mode 100644
index 0000000..b62a14a
--- /dev/null
+++ b/chrome/browser/sync/util/data_encryption.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2009 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 CHROME_BROWSER_SYNC_UTIL_DATA_ENCRYPTION_H_
+#define CHROME_BROWSER_SYNC_UTIL_DATA_ENCRYPTION_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "chrome/browser/sync/util/sync_types.h"
+
+using std::string;
+using std::vector;
+
+vector<uint8> EncryptData(const string& data);
+bool DecryptData(const vector<uint8>& in_data, string* out_data);
+
+#endif // CHROME_BROWSER_SYNC_UTIL_DATA_ENCRYPTION_H_
diff --git a/chrome/browser/sync/util/data_encryption_unittest.cc b/chrome/browser/sync/util/data_encryption_unittest.cc
new file mode 100644
index 0000000..63bfda5
--- /dev/null
+++ b/chrome/browser/sync/util/data_encryption_unittest.cc
@@ -0,0 +1,31 @@
+// Copyright (c) 2009 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 "chrome/browser/sync/util/data_encryption.h"
+
+#include <string>
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+using std::vector;
+
+namespace {
+
+TEST(DataEncryption, TestEncryptDecryptOfSampleString) {
+ vector<uint8> example(EncryptData("example"));
+ ASSERT_FALSE(example.empty());
+ string result;
+ ASSERT_TRUE(DecryptData(example, &result));
+ ASSERT_TRUE(result == "example");
+}
+
+TEST(DataEncryption, TestDecryptFailure) {
+ vector<uint8> example(0, 0);
+ string result;
+ ASSERT_FALSE(DecryptData(example, &result));
+}
+
+} // namespace
diff --git a/chrome/browser/sync/util/dbgq.h b/chrome/browser/sync/util/dbgq.h
new file mode 100644
index 0000000..65ebb5c
--- /dev/null
+++ b/chrome/browser/sync/util/dbgq.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2009 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 CHROME_BROWSER_SYNC_UTIL_DBGQ_H_
+#define CHROME_BROWSER_SYNC_UTIL_DBGQ_H_
+
+#include "base/basictypes.h" // for COMPILE_ASSERT
+
+// A circular queue that is designed to be easily inspectable in a debugger. It
+// puts the elements into the array in reverse, so you can just look at the i_
+// pointer for a recent history.
+template <typename T, size_t size>
+class DebugQueue {
+ COMPILE_ASSERT(size > 0, DebugQueue_size_must_be_greater_than_zero);
+ public:
+ DebugQueue() : i_(array_) { }
+ void Push(const T& t) {
+ i_ = (array_ == i_ ? array_ + size - 1 : i_ - 1);
+ *i_ = t;
+ }
+ protected:
+ T* i_; // Points to the newest element in the queue.
+ T array_[size];
+};
+
+#endif // CHROME_BROWSER_SYNC_UTIL_DBGQ_H_
diff --git a/chrome/browser/sync/util/event_sys-inl.h b/chrome/browser/sync/util/event_sys-inl.h
new file mode 100644
index 0000000..c361528
--- /dev/null
+++ b/chrome/browser/sync/util/event_sys-inl.h
@@ -0,0 +1,340 @@
+// Copyright (c) 2009 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 CHROME_BROWSER_SYNC_UTIL_EVENT_SYS_INL_H_
+#define CHROME_BROWSER_SYNC_UTIL_EVENT_SYS_INL_H_
+
+#include <map>
+
+#include "base/atomicops.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/port.h"
+#include "chrome/browser/sync/util/compat-pthread.h"
+#include "chrome/browser/sync/util/event_sys.h"
+#include "chrome/browser/sync/util/pthread_helpers.h"
+#include "chrome/browser/sync/util/sync_types.h"
+
+// How to use Channels:
+
+// 0. Assume Bob is the name of the class from which you want to broadcast
+// events.
+// 1. Choose an EventType. This could be an Enum or something more complicated.
+// 2. Create an EventTraits class for your EventType. It must have
+// two members:
+//
+// typedef x EventType;
+// static bool IsChannelShutdownEvent(const EventType& event);
+//
+// 3. Add an EventChannel<MyEventTraits>* instance and event_channel() const;
+// accessor to Bob.
+// Delete the channel ordinarily in Bob's destructor, or whenever you want.
+// 4. To broadcast events, call bob->event_channel()->NotifyListeners(event).
+// 5. Only call NotifyListeners from a single thread at a time.
+
+// How to use Listeners/Hookups:
+
+// 0. Assume you want a class called Lisa to listen to events from Bob.
+// 1. Create an event handler method in Lisa. Its single argument should be of
+// your event type.
+// 2. Add a EventListenerHookup* hookup_ member to Lisa.
+// 3. Initialize the hookup by calling:
+//
+// hookup_ = NewEventListenerHookup(bob->event_channel(),
+// this,
+// &Lisa::HandleEvent);
+//
+// 4. Delete hookup_ in Lisa's destructor, or anytime sooner to stop receiving
+// events.
+
+// An Event Channel is a source, or broadcaster of events. Listeners subscribe
+// by calling the AddListener() method. The owner of the channel calls the
+// NotifyListeners() method.
+//
+// Don't inherit from this class. Just make an event_channel member and an
+// event_channel() accessor.
+
+// No reason why CallbackWaiters has to be templatized.
+class CallbackWaiters {
+ public:
+ CallbackWaiters() : waiter_count_(0), callback_done_(false) {
+ }
+ ~CallbackWaiters() {
+ DCHECK_EQ(0, waiter_count_);
+ }
+ void WaitForCallbackToComplete(PThreadMutex* listeners_mutex) {
+ {
+ PThreadScopedLock<PThreadMutex> lock(&mutex_);
+ waiter_count_ += 1;
+ listeners_mutex->Unlock();
+ while (!callback_done_)
+ pthread_cond_wait(&condvar_.condvar_, &mutex_.mutex_);
+ waiter_count_ -= 1;
+ if (0 != waiter_count_)
+ return;
+ }
+ delete this;
+ }
+
+ void Signal() {
+ PThreadScopedLock<PThreadMutex> lock(&mutex_);
+ callback_done_ = true;
+ pthread_cond_broadcast(&condvar_.condvar_);
+ }
+
+ protected:
+ int waiter_count_;
+ bool callback_done_;
+ PThreadMutex mutex_;
+ PThreadCondVar condvar_;
+};
+
+template <typename EventTraitsType, typename NotifyLock,
+ typename ScopedNotifyLocker>
+class EventChannel {
+ public:
+ typedef EventTraitsType EventTraits;
+ typedef typename EventTraits::EventType EventType;
+ typedef EventListener<EventType> Listener;
+
+ protected:
+ typedef std::map<Listener*, bool> Listeners;
+ typedef PThreadScopedLock<PThreadMutex> ScopedListenersLock;
+
+ public:
+ // The shutdown event gets send in the EventChannel's destructor.
+ explicit EventChannel(const EventType& shutdown_event)
+ : callback_waiters_(NULL), shutdown_event_(shutdown_event),
+ current_listener_callback_(NULL) {
+ }
+
+ ~EventChannel() {
+ // Tell all the listeners that the channel is being deleted.
+ NotifyListeners(shutdown_event_);
+
+ // Make sure all the listeners have been disconnected. Otherwise, they
+ // will try to call RemoveListener() at a later date.
+#ifdef DEBUG
+ ScopedListenersLock lock(&listeners_mutex_);
+ for (typename Listeners::iterator i = listeners_.begin();
+ i != listeners_.end(); ++i) {
+ DCHECK(i->second) << "Listener not disconnected";
+ }
+#endif
+ }
+
+ // Never call this twice for the same listener.
+ //
+ // Thread safe.
+ void AddListener(Listener* listener) {
+ ScopedListenersLock lock(&listeners_mutex_);
+ typename Listeners::iterator found = listeners_.find(listener);
+ if (found == listeners_.end()) {
+ listeners_.insert(std::make_pair(listener,
+ false)); // Not dead yet.
+ } else {
+ DCHECK(found->second) << "Attempted to add the same listener twice.";
+ found->second = false; // Not dead yet.
+ }
+ }
+
+ // If listener's callback is currently executing, this method waits until the
+ // callback completes before returning.
+ //
+ // Thread safe.
+ void RemoveListener(Listener* listener) {
+ bool wait = false;
+ listeners_mutex_.Lock();
+ typename Listeners::iterator found = listeners_.find(listener);
+ if (found != listeners_.end()) {
+ found->second = true; // Mark as dead.
+ wait = (found->first == current_listener_callback_ &&
+ (!pthread_equal(current_listener_callback_thread_id_,
+ pthread_self())));
+ }
+ if (!wait) {
+ listeners_mutex_.Unlock();
+ return;
+ }
+ if (NULL == callback_waiters_)
+ callback_waiters_ = new CallbackWaiters;
+ callback_waiters_->WaitForCallbackToComplete(&listeners_mutex_);
+ }
+
+ // Blocks until all listeners have been notified.
+ //
+ // NOT thread safe. Must only be called by one thread at a time.
+ void NotifyListeners(const EventType& event) {
+ ScopedNotifyLocker lock_notify(&notify_lock_);
+ listeners_mutex_.Lock();
+ DCHECK(NULL == current_listener_callback_);
+ current_listener_callback_thread_id_ = pthread_self();
+ typename Listeners::iterator i = listeners_.begin();
+ while (i != listeners_.end()) {
+ if (i->second) { // Clean out dead listeners
+ listeners_.erase(i++);
+ continue;
+ }
+ current_listener_callback_ = i->first;
+ listeners_mutex_.Unlock();
+
+ i->first->HandleEvent(event);
+
+ listeners_mutex_.Lock();
+ current_listener_callback_ = NULL;
+ if (NULL != callback_waiters_) {
+ callback_waiters_->Signal();
+ callback_waiters_ = NULL;
+ }
+
+ ++i;
+ }
+ listeners_mutex_.Unlock();
+ }
+
+ // A map iterator remains valid until the element it points to gets removed
+ // from the map, so a map is perfect for our needs.
+ //
+ // Map value is a bool, true means the Listener is dead.
+ Listeners listeners_;
+ // NULL means no callback is currently being called.
+ Listener* current_listener_callback_;
+ // Only valid when current_listener is not NULL.
+ // The thread on which the callback is executing.
+ pthread_t current_listener_callback_thread_id_;
+ // Win32 Event that is usually NULL. Only created when another thread calls
+ // Remove while in callback. Owned and closed by the thread calling Remove().
+ CallbackWaiters* callback_waiters_;
+
+ PThreadMutex listeners_mutex_; // Protects all members above.
+ const EventType shutdown_event_;
+ NotifyLock notify_lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(EventChannel);
+};
+
+// An EventListenerHookup hooks up a method in your class to an EventChannel.
+// Deleting the hookup disconnects from the EventChannel.
+//
+// Contains complexity of inheriting from Listener class and managing lifetimes.
+//
+// Create using NewEventListenerHookup() to avoid explicit template arguments.
+class EventListenerHookup {
+ public:
+ virtual ~EventListenerHookup() { }
+};
+
+template <typename EventChannel, typename EventTraits,
+ class Derived>
+class EventListenerHookupImpl : public EventListenerHookup,
+public EventListener<typename EventTraits::EventType> {
+ public:
+ explicit EventListenerHookupImpl(EventChannel* channel)
+ : channel_(channel), deleted_(NULL) {
+ channel->AddListener(this);
+ connected_ = true;
+ }
+
+ ~EventListenerHookupImpl() {
+ if (NULL != deleted_)
+ *deleted_ = true;
+ if (connected_)
+ channel_->RemoveListener(this);
+ }
+
+ typedef typename EventTraits::EventType EventType;
+ virtual void HandleEvent(const EventType& event) {
+ DCHECK(connected_);
+ bool deleted = false;
+ deleted_ = &deleted;
+ static_cast<Derived*>(this)->Callback(event);
+ if (deleted) // The callback (legally) deleted this.
+ return; // The only safe thing to do.
+ deleted_ = NULL;
+ if (EventTraits::IsChannelShutdownEvent(event)) {
+ channel_->RemoveListener(this);
+ connected_ = false;
+ }
+ }
+
+ protected:
+ EventChannel* const channel_;
+ bool connected_;
+ bool* deleted_; // Allows the handler to delete the hookup.
+};
+
+// SimpleHookup just passes the event to the callback message.
+template <typename EventChannel, typename EventTraits,
+ typename CallbackObject, typename CallbackMethod>
+class SimpleHookup
+ : public EventListenerHookupImpl<EventChannel, EventTraits,
+ SimpleHookup<EventChannel,
+ EventTraits,
+ CallbackObject,
+ CallbackMethod> > {
+ public:
+ SimpleHookup(EventChannel* channel, CallbackObject* cbobject,
+ CallbackMethod cbmethod)
+ : EventListenerHookupImpl<EventChannel, EventTraits,
+ SimpleHookup>(channel), cbobject_(cbobject),
+ cbmethod_(cbmethod) { }
+
+ typedef typename EventTraits::EventType EventType;
+ void Callback(const EventType& event) {
+ (cbobject_->*cbmethod_)(event);
+ }
+ CallbackObject* const cbobject_;
+ CallbackMethod const cbmethod_;
+};
+
+// ArgHookup also passes an additional arg to the callback method.
+template <typename EventChannel, typename EventTraits,
+ typename CallbackObject, typename CallbackMethod,
+ typename CallbackArg0>
+class ArgHookup :
+ public EventListenerHookupImpl<EventChannel, EventTraits,
+ ArgHookup<EventChannel, EventTraits,
+ CallbackObject,
+ CallbackMethod,
+ CallbackArg0> > {
+ public:
+ ArgHookup(EventChannel* channel, CallbackObject* cbobject,
+ CallbackMethod cbmethod, CallbackArg0 arg0)
+ : EventListenerHookupImpl<EventChannel, EventTraits,
+ ArgHookup>(channel), cbobject_(cbobject),
+ cbmethod_(cbmethod), arg0_(arg0) { }
+
+ typedef typename EventTraits::EventType EventType;
+ void Callback(const EventType& event) {
+ (cbobject_->*cbmethod_)(arg0_, event);
+ }
+ CallbackObject* const cbobject_;
+ CallbackMethod const cbmethod_;
+ CallbackArg0 const arg0_;
+};
+
+
+template <typename EventChannel, typename CallbackObject,
+ typename CallbackMethod>
+EventListenerHookup* NewEventListenerHookup(EventChannel* channel,
+ CallbackObject* cbobject,
+ CallbackMethod cbmethod) {
+ return new SimpleHookup<EventChannel,
+ typename EventChannel::EventTraits,
+ CallbackObject, CallbackMethod>(channel, cbobject, cbmethod);
+}
+
+template <typename EventChannel, typename CallbackObject,
+ typename CallbackMethod, typename CallbackArg0>
+EventListenerHookup* NewEventListenerHookup(EventChannel* channel,
+ CallbackObject* cbobject,
+ CallbackMethod cbmethod,
+ CallbackArg0 arg0) {
+ return new ArgHookup<EventChannel,
+ typename EventChannel::EventTraits,
+ CallbackObject, CallbackMethod, CallbackArg0>(channel, cbobject,
+ cbmethod, arg0);
+}
+
+#endif // CHROME_BROWSER_SYNC_UTIL_EVENT_SYS_INL_H_
diff --git a/chrome/browser/sync/util/event_sys.h b/chrome/browser/sync/util/event_sys.h
new file mode 100644
index 0000000..5dcf44a
--- /dev/null
+++ b/chrome/browser/sync/util/event_sys.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2009 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 CHROME_BROWSER_SYNC_UTIL_EVENT_SYS_H_
+#define CHROME_BROWSER_SYNC_UTIL_EVENT_SYS_H_
+
+#include "chrome/browser/sync/util/pthread_helpers_fwd.h"
+
+// An abstract base class for listening to events.
+//
+// Don't inherit from this class yourself. Using NewEventListenerHookup() is
+// much easier.
+template <typename EventType>
+class EventListener {
+ public:
+ virtual void HandleEvent(const EventType& event) = 0;
+};
+
+// See the -inl.h for details about the following.
+
+template <typename EventTraits, typename NotifyLock = PThreadNoLock,
+ typename ScopedNotifyLocker = PThreadScopedLock<NotifyLock> >
+class EventChannel;
+
+class EventListenerHookup;
+
+template <typename EventChannel, typename CallbackObject,
+ typename CallbackMethod>
+EventListenerHookup* NewEventListenerHookup(EventChannel* channel,
+ CallbackObject* cbobject,
+ CallbackMethod cbmethod);
+
+template <typename EventChannel, typename CallbackObject,
+ typename CallbackMethod, typename CallbackArg0>
+EventListenerHookup* NewEventListenerHookup(EventChannel* channel,
+ CallbackObject* cbobject,
+ CallbackMethod cbmethod,
+ CallbackArg0 arg0);
+
+#endif // CHROME_BROWSER_SYNC_UTIL_EVENT_SYS_H_
diff --git a/chrome/browser/sync/util/event_sys_unittest.cc b/chrome/browser/sync/util/event_sys_unittest.cc
new file mode 100644
index 0000000..5e521b1
--- /dev/null
+++ b/chrome/browser/sync/util/event_sys_unittest.cc
@@ -0,0 +1,271 @@
+// Copyright (c) 2009 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 <iosfwd>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/port.h"
+#include "chrome/browser/sync/util/event_sys-inl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::endl;
+using std::ostream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+namespace {
+
+class Pair;
+
+struct TestEvent {
+ Pair* source;
+ enum {
+ A_CHANGED, B_CHANGED, PAIR_BEING_DELETED,
+ } what_happened;
+ int old_value;
+};
+
+struct TestEventTraits {
+ typedef TestEvent EventType;
+ static bool IsChannelShutdownEvent(const TestEvent& event) {
+ return TestEvent::PAIR_BEING_DELETED == event.what_happened;
+ }
+};
+
+class Pair {
+ public:
+ typedef EventChannel<TestEventTraits> Channel;
+ explicit Pair(const string& name) : name_(name), a_(0), b_(0) {
+ TestEvent shutdown = { this, TestEvent::PAIR_BEING_DELETED, 0 };
+ event_channel_ = new Channel(shutdown);
+ }
+ ~Pair() {
+ delete event_channel_;
+ }
+ void set_a(int n) {
+ TestEvent event = { this, TestEvent::A_CHANGED, a_ };
+ a_ = n;
+ event_channel_->NotifyListeners(event);
+ }
+ void set_b(int n) {
+ TestEvent event = { this, TestEvent::B_CHANGED, b_ };
+ b_ = n;
+ event_channel_->NotifyListeners(event);
+ }
+ int a() const { return a_; }
+ int b() const { return b_; }
+ const string& name() { return name_; }
+ Channel* event_channel() const { return event_channel_; }
+
+ protected:
+ const string name_;
+ int a_;
+ int b_;
+ Channel* event_channel_;
+};
+
+class EventLogger {
+ public:
+ explicit EventLogger(ostream& out) : out_(out) { }
+ ~EventLogger() {
+ for (Hookups::iterator i = hookups_.begin(); i != hookups_.end(); ++i)
+ delete *i;
+ }
+
+ void Hookup(const string name, Pair::Channel* channel) {
+ hookups_.push_back(NewEventListenerHookup(channel, this,
+ &EventLogger::HandlePairEvent,
+ name));
+ }
+
+ void HandlePairEvent(const string& name, const TestEvent& event) {
+ const char* what_changed;
+ int new_value;
+ Hookups::iterator dead;
+ switch (event.what_happened) {
+ case TestEvent::A_CHANGED:
+ what_changed = "A";
+ new_value = event.source->a();
+ break;
+ case TestEvent::B_CHANGED:
+ what_changed = "B";
+ new_value = event.source->b();
+ break;
+ case TestEvent::PAIR_BEING_DELETED:
+ out_ << name << " heard " << event.source->name() << " being deleted."
+ << endl;
+ return;
+ default:
+ LOG(FATAL) << "Bad event.what_happened: " << event.what_happened;
+ break;
+ }
+ out_ << name << " heard " << event.source->name() << "'s " << what_changed
+ << " change from "
+ << event.old_value << " to " << new_value << endl;
+ }
+
+ typedef vector<EventListenerHookup*> Hookups;
+ Hookups hookups_;
+ ostream& out_;
+};
+
+const char golden_result[] = "Larry heard Sally's B change from 0 to 2\n"
+"Larry heard Sally's A change from 1 to 3\n"
+"Lewis heard Sam's B change from 0 to 5\n"
+"Larry heard Sally's A change from 3 to 6\n"
+"Larry heard Sally being deleted.\n";
+
+TEST(EventSys, Basic) {
+ Pair sally("Sally"), sam("Sam");
+ sally.set_a(1);
+ stringstream log;
+ EventLogger logger(log);
+ logger.Hookup("Larry", sally.event_channel());
+ sally.set_b(2);
+ sally.set_a(3);
+ sam.set_a(4);
+ logger.Hookup("Lewis", sam.event_channel());
+ sam.set_b(5);
+ sally.set_a(6);
+ // Test that disconnect within callback doesn't deadlock.
+ TestEvent event = {&sally, TestEvent::PAIR_BEING_DELETED, 0 };
+ sally.event_channel()->NotifyListeners(event);
+ sally.set_a(7);
+ ASSERT_EQ(log.str(), golden_result);
+}
+
+
+// This goes pretty far beyond the normal use pattern, so don't use
+// ThreadTester as an example of what to do.
+class ThreadTester : public EventListener<TestEvent> {
+ public:
+ explicit ThreadTester(Pair* pair)
+ : pair_(pair), remove_event_bool_(false) {
+ pair_->event_channel()->AddListener(this);
+ }
+ ~ThreadTester() {
+ pair_->event_channel()->RemoveListener(this);
+ for (int i = 0; i < threads_.size(); i++) {
+ CHECK(pthread_join(threads_[i].thread, NULL) == 0);
+ delete threads_[i].completed;
+ }
+ }
+
+ struct ThreadInfo {
+ pthread_t thread;
+ bool *completed;
+ };
+
+ struct ThreadArgs {
+ ThreadTester* self;
+ pthread_cond_t *thread_running_cond;
+ pthread_mutex_t *thread_running_mutex;
+ bool *thread_running;
+ bool *completed;
+ };
+
+ pthread_t Go() {
+ PThreadCondVar thread_running_cond;
+ PThreadMutex thread_running_mutex;
+ ThreadArgs args;
+ ThreadInfo info;
+ info.completed = new bool(false);
+ args.self = this;
+ args.completed = info.completed;
+ args.thread_running_cond = &(thread_running_cond.condvar_);
+ args.thread_running_mutex = &(thread_running_mutex.mutex_);
+ args.thread_running = new bool(false);
+ CHECK(0 ==
+ pthread_create(&info.thread, NULL, ThreadTester::ThreadMain, &args));
+ thread_running_mutex.Lock();
+ while ((*args.thread_running) == false) {
+ pthread_cond_wait(&(thread_running_cond.condvar_),
+ &(thread_running_mutex.mutex_));
+ }
+ thread_running_mutex.Unlock();
+ delete args.thread_running;
+ threads_.push_back(info);
+ return info.thread;
+ }
+
+ static void* ThreadMain(void* arg) {
+ ThreadArgs args = *reinterpret_cast<ThreadArgs*>(arg);
+ pthread_mutex_lock(args.thread_running_mutex);
+ *args.thread_running = true;
+ pthread_cond_signal(args.thread_running_cond);
+ pthread_mutex_unlock(args.thread_running_mutex);
+
+ args.self->remove_event_mutex_.Lock();
+ while (args.self->remove_event_bool_ == false) {
+ pthread_cond_wait(&args.self->remove_event_.condvar_,
+ &args.self->remove_event_mutex_.mutex_);
+ }
+ args.self->remove_event_mutex_.Unlock();
+
+ // Normally, you'd just delete the hookup. This is very bad style, but
+ // necessary for the test.
+ args.self->pair_->event_channel()->RemoveListener(args.self);
+ *args.completed = true;
+ return 0;
+ }
+
+ void HandleEvent(const TestEvent& event) {
+ remove_event_mutex_.Lock();
+ remove_event_bool_ = true;
+ pthread_cond_broadcast(&remove_event_.condvar_);
+ remove_event_mutex_.Unlock();
+
+ // Windows and posix use different functions to sleep.
+#ifdef OS_WINDOWS
+ Sleep(1);
+#else
+ sleep(1);
+#endif
+
+ for (int i = 0; i < threads_.size(); i++) {
+ if (*(threads_[i].completed))
+ LOG(FATAL) << "A test thread exited too early.";
+ }
+ }
+
+ Pair* pair_;
+ PThreadCondVar remove_event_;
+ PThreadMutex remove_event_mutex_;
+ bool remove_event_bool_;
+ vector<ThreadInfo> threads_;
+};
+
+TEST(EventSys, Multithreaded) {
+ Pair sally("Sally");
+ ThreadTester a(&sally);
+ for (int i = 0; i < 3; ++i)
+ a.Go();
+ sally.set_b(99);
+}
+
+class HookupDeleter {
+ public:
+ void HandleEvent(const TestEvent& event) {
+ delete hookup_;
+ hookup_ = NULL;
+ }
+ EventListenerHookup* hookup_;
+};
+
+TEST(EventSys, InHandlerDeletion) {
+ Pair sally("Sally");
+ HookupDeleter deleter;
+ deleter.hookup_ = NewEventListenerHookup(sally.event_channel(),
+ &deleter,
+ &HookupDeleter::HandleEvent);
+ sally.set_a(1);
+ ASSERT_TRUE(NULL == deleter.hookup_);
+}
+
+} // namespace
diff --git a/chrome/browser/sync/util/fast_dump.h b/chrome/browser/sync/util/fast_dump.h
new file mode 100644
index 0000000..9266f0e
--- /dev/null
+++ b/chrome/browser/sync/util/fast_dump.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2009 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 CHROME_BROWSER_SYNC_UTIL_FAST_DUMP_H_
+#define CHROME_BROWSER_SYNC_UTIL_FAST_DUMP_H_
+
+#include <ostream>
+#include <streambuf>
+#include <string>
+
+#include "base/string_util.h"
+
+using std::ostream;
+using std::streambuf;
+using std::string;
+
+// This seems totally gratuitous, and it would be if std::ostream was fast, but
+// std::ostream is slow. It's slow because it creates a ostream::sentry (mutex)
+// for every little << operator. When you want to dump a whole lot of stuff
+// under a single mutex lock, use this FastDump class.
+namespace browser_sync {
+class FastDump {
+ public:
+ explicit FastDump(ostream* outs) : sentry_(*outs), out_(outs->rdbuf()) {
+ }
+ ostream::sentry sentry_;
+ streambuf* const out_;
+};
+} // namespace browser_sync
+
+inline browser_sync::FastDump& operator <<
+ (browser_sync::FastDump& dump, int64 n) {
+ string numbuf(Int64ToString(n));
+ const char* number = numbuf.c_str();
+ dump.out_->sputn(number, numbuf.length());
+ return dump;
+}
+
+inline browser_sync::FastDump& operator <<
+ (browser_sync::FastDump& dump, int32 n) {
+ string numbuf(IntToString(n));
+ const char* number = numbuf.c_str();
+ dump.out_->sputn(number, numbuf.length());
+ return dump;
+}
+
+inline browser_sync::FastDump& operator <<
+ (browser_sync::FastDump& dump, const char* s) {
+ dump.out_->sputn(s, strlen(s));
+ return dump;
+}
+
+inline browser_sync::FastDump& operator <<
+ (browser_sync::FastDump& dump, const string& s) {
+ dump.out_->sputn(s.data(), s.size());
+ return dump;
+}
+
+#endif // CHROME_BROWSER_SYNC_UTIL_FAST_DUMP_H_
diff --git a/chrome/browser/sync/util/highres_timer-linux.cc b/chrome/browser/sync/util/highres_timer-linux.cc
new file mode 100644
index 0000000..8fed343
--- /dev/null
+++ b/chrome/browser/sync/util/highres_timer-linux.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2009 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.
+
+// High resolution timer functions for use in Linux.
+
+#include "chrome/browser/sync/util/highres_timer.h"
+
+const uint64 MICROS_IN_MILLI = 1000L;
+const uint64 MICROS_IN_HALF_MILLI = 500L;
+const uint64 MICROS_IN_HALF_SECOND = 500000L;
+
+uint64 HighresTimer::GetElapsedMs() const {
+ uint64 end_time = GetCurrentTicks();
+
+ // Scale to ms and round to nearest ms - rounding is important because
+ // otherwise the truncation error may accumulate e.g. in sums.
+ return (uint64(end_time - start_ticks_) + MICROS_IN_HALF_MILLI) /
+ MICROS_IN_MILLI;
+}
+
+uint64 HighresTimer::GetElapsedSec() const {
+ uint64 end_time = GetCurrentTicks();
+
+ // Scale to ms and round to nearest ms - rounding is important because
+ // otherwise the truncation error may accumulate e.g. in sums.
+ return (uint64(end_time - start_ticks_) + MICROS_IN_HALF_SECOND) /
+ MICROS_IN_SECOND;
+}
diff --git a/chrome/browser/sync/util/highres_timer-linux.h b/chrome/browser/sync/util/highres_timer-linux.h
new file mode 100644
index 0000000..01a022d
--- /dev/null
+++ b/chrome/browser/sync/util/highres_timer-linux.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2009 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.
+//
+// High resolution timer functions for use in Linux.
+
+#ifndef CHROME_BROWSER_SYNC_UTIL_HIGHRES_TIMER_LINUX_H_
+#define CHROME_BROWSER_SYNC_UTIL_HIGHRES_TIMER_LINUX_H_
+
+#include "base/basictypes.h"
+
+#include <sys/time.h>
+
+const uint64 MICROS_IN_SECOND = 1000000L;
+
+// A handy class for reliably measuring wall-clock time with decent resolution.
+//
+// We want to measure time with high resolution on Linux. What to do?
+//
+// RDTSC? Sure, but how do you convert it to wall clock time?
+// clock_gettime? It's not in all Linuxes.
+//
+// Let's just use gettimeofday; it's good to the microsecond.
+class HighresTimer {
+ public:
+ // Captures the current start time.
+ HighresTimer();
+
+ // Captures the current tick, can be used to reset a timer for reuse.
+ void Start();
+
+ // Returns the elapsed ticks with full resolution.
+ uint64 GetElapsedTicks() const;
+
+ // Returns the elapsed time in milliseconds, rounded to the nearest
+ // millisecond.
+ uint64 GetElapsedMs() const;
+
+ // Returns the elapsed time in seconds, rounded to the nearest second.
+ uint64 GetElapsedSec() const;
+
+ uint64 start_ticks() const { return start_ticks_; }
+
+ // Returns timer frequency from cache, should be less overhead than
+ // ::QueryPerformanceFrequency.
+ static uint64 GetTimerFrequency();
+ // Returns current ticks.
+ static uint64 GetCurrentTicks();
+
+ private:
+ // Captured start time.
+ uint64 start_ticks_;
+};
+
+inline HighresTimer::HighresTimer() {
+ Start();
+}
+
+inline void HighresTimer::Start() {
+ start_ticks_ = GetCurrentTicks();
+}
+
+inline uint64 HighresTimer::GetTimerFrequency() {
+ // Fixed; one "tick" is one microsecond.
+ return MICROS_IN_SECOND;
+}
+
+inline uint64 HighresTimer::GetCurrentTicks() {
+ timeval tv;
+ gettimeofday(&tv, 0);
+
+ return tv.tv_sec * MICROS_IN_SECOND + tv.tv_usec;
+}
+
+inline uint64 HighresTimer::GetElapsedTicks() const {
+ return start_ticks_ - GetCurrentTicks();
+}
+
+#endif // CHROME_BROWSER_SYNC_UTIL_HIGHRES_TIMER_LINUX_H_
diff --git a/chrome/browser/sync/util/highres_timer-win32.cc b/chrome/browser/sync/util/highres_timer-win32.cc
new file mode 100644
index 0000000..0d7323a
--- /dev/null
+++ b/chrome/browser/sync/util/highres_timer-win32.cc
@@ -0,0 +1,46 @@
+// Copyright (c) 2009 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.
+//
+// High resolution timer functions for use in Windows.
+
+#include "chrome/browser/sync/util/highres_timer.h"
+
+bool HighresTimer::perf_freq_collected_ = false;
+ULONGLONG HighresTimer::perf_freq_ = 0;
+
+ULONGLONG HighresTimer::GetElapsedMs() const {
+ ULONGLONG end_time = GetCurrentTicks();
+
+ // Scale to ms and round to nearest ms - rounding is important because
+ // otherwise the truncation error may accumulate e.g. in sums.
+ //
+ // Given infinite resolution, this expression could be written as:
+ // trunc((end - start (units:freq*sec))/freq (units:sec) *
+ // 1000 (unit:ms) + 1/2 (unit:ms))
+ ULONGLONG freq = GetTimerFrequency();
+ return ((end_time - start_ticks_) * 1000L + freq / 2) / freq;
+}
+
+ULONGLONG HighresTimer::GetElapsedSec() const {
+ ULONGLONG end_time = GetCurrentTicks();
+
+ // Round to nearest ms - rounding is important because otherwise the
+ // truncation error may accumulate e.g. in sums.
+ //
+ // Given infinite resolution, this expression could be written as:
+ // trunc((end - start (units:freq*sec))/freq (unit:sec) + 1/2 (unit:sec))
+ ULONGLONG freq = GetTimerFrequency();
+ return ((end_time - start_ticks_) + freq / 2) / freq;
+}
+
+void HighresTimer::CollectPerfFreq() {
+ LARGE_INTEGER freq;
+
+ // Note that this is racy. It's OK, however, because even concurrent
+ // executions of this are idempotent.
+ if (::QueryPerformanceFrequency(&freq)) {
+ perf_freq_ = freq.QuadPart;
+ perf_freq_collected_ = true;
+ }
+}
diff --git a/chrome/browser/sync/util/highres_timer-win32.h b/chrome/browser/sync/util/highres_timer-win32.h
new file mode 100644
index 0000000..6e87ce9
--- /dev/null
+++ b/chrome/browser/sync/util/highres_timer-win32.h
@@ -0,0 +1,78 @@
+// Copyright (c) 2009 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.
+//
+// High resolution timer functions for use in Windows.
+
+#ifndef CHROME_BROWSER_SYNC_UTIL_HIGHRES_TIMER_WIN32_H_
+#define CHROME_BROWSER_SYNC_UTIL_HIGHRES_TIMER_WIN32_H_
+
+#include <windows.h>
+
+// A handy class for reliably measuring wall-clock time with decent resolution,
+// even on multi-processor machines and on laptops (where RDTSC potentially
+// returns different results on different processors and/or the RDTSC timer
+// clocks at different rates depending on the power state of the CPU,
+// respectively).
+class HighresTimer {
+ public:
+ // Captures the current start time.
+ HighresTimer();
+
+ // Captures the current tick, can be used to reset a timer for reuse.
+ void Start();
+
+ // Returns the elapsed ticks with full resolution.
+ ULONGLONG GetElapsedTicks() const;
+
+ // Returns the elapsed time in milliseconds, rounded to the nearest
+ // millisecond.
+ ULONGLONG GetElapsedMs() const;
+
+ // Returns the elapsed time in seconds, rounded to the nearest second.
+ ULONGLONG GetElapsedSec() const;
+
+ ULONGLONG start_ticks() const { return start_ticks_; }
+
+ // Returns timer frequency from cache, should be less
+ // overhead than ::QueryPerformanceFrequency.
+ static ULONGLONG GetTimerFrequency();
+ // Returns current ticks.
+ static ULONGLONG GetCurrentTicks();
+
+ private:
+ static void CollectPerfFreq();
+
+ // Captured start time.
+ ULONGLONG start_ticks_;
+
+ // Captured performance counter frequency.
+ static bool perf_freq_collected_;
+ static ULONGLONG perf_freq_;
+};
+
+inline HighresTimer::HighresTimer() {
+ Start();
+}
+
+inline void HighresTimer::Start() {
+ start_ticks_ = GetCurrentTicks();
+}
+
+inline ULONGLONG HighresTimer::GetTimerFrequency() {
+ if (!perf_freq_collected_)
+ CollectPerfFreq();
+ return perf_freq_;
+}
+
+inline ULONGLONG HighresTimer::GetCurrentTicks() {
+ LARGE_INTEGER ticks;
+ ::QueryPerformanceCounter(&ticks);
+ return ticks.QuadPart;
+}
+
+inline ULONGLONG HighresTimer::GetElapsedTicks() const {
+ return start_ticks_ - GetCurrentTicks();
+}
+
+#endif // CHROME_BROWSER_SYNC_UTIL_HIGHRES_TIMER_WIN32_H_
diff --git a/chrome/browser/sync/util/highres_timer.h b/chrome/browser/sync/util/highres_timer.h
new file mode 100644
index 0000000..e2bde4e
--- /dev/null
+++ b/chrome/browser/sync/util/highres_timer.h
@@ -0,0 +1,13 @@
+// Copyright (c) 2009 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.
+//
+// High resolution timer functions defined for each OS.
+
+#if defined(OS_WINDOWS)
+#include "chrome/browser/sync/util/highres_timer-win32.h"
+#elif defined(OS_MACOSX)
+#error "Mac timer functions are missing."
+#else
+#include "chrome/browser/sync/util/highres_timer-linux.h"
+#endif
diff --git a/chrome/browser/sync/util/highres_timer_unittest.cc b/chrome/browser/sync/util/highres_timer_unittest.cc
new file mode 100644
index 0000000..5723e7f8
--- /dev/null
+++ b/chrome/browser/sync/util/highres_timer_unittest.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2009 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.
+//
+// High resolution timer unit tests.
+
+#include "base/basictypes.h"
+#include "chrome/browser/sync/util/highres_timer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// These unittests have proven to be flaky on buildbot. While we don't want
+// them breaking the build, we still build them to guard against bitrot.
+// On dev machines during local builds we can enable them.
+TEST(HighresTimer, DISABLED_MillisecondClock) {
+ HighresTimer timer;
+
+ // note: this could fail if we context switch between initializing the timer
+ // and here. Very unlikely however.
+ EXPECT_EQ(0, timer.GetElapsedMs());
+ timer.Start();
+ uint64 half_ms = HighresTimer::GetTimerFrequency() / 2000;
+ // busy wait for half a millisecond
+ while (timer.start_ticks() + half_ms > HighresTimer::GetCurrentTicks()) {
+ // Nothing
+ }
+ EXPECT_EQ(1, timer.GetElapsedMs());
+}
+
+TEST(HighresTimer, DISABLED_SecondClock) {
+ HighresTimer timer;
+
+ EXPECT_EQ(0, timer.GetElapsedSec());
+#ifdef OS_WINDOWS
+ ::Sleep(250);
+#else
+ struct timespec ts1 = {0, 250000000};
+ nanosleep(&ts1, 0);
+#endif
+ EXPECT_EQ(0, timer.GetElapsedSec());
+ EXPECT_LE(230, timer.GetElapsedMs());
+ EXPECT_GE(270, timer.GetElapsedMs());
+#ifdef OS_WINDOWS
+ ::Sleep(251);
+#else
+ struct timespec ts2 = {0, 251000000};
+ nanosleep(&ts2, 0);
+#endif
+ EXPECT_EQ(1, timer.GetElapsedSec());
+}
diff --git a/chrome/browser/sync/util/path_helpers-linux.cc b/chrome/browser/sync/util/path_helpers-linux.cc
new file mode 100644
index 0000000..4f4543d
--- /dev/null
+++ b/chrome/browser/sync/util/path_helpers-linux.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2009 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 <sys/types.h>
+
+#include <glib.h>
+#include <string.h>
+
+#include "base/logging.h"
+#include "base/port.h"
+#include "chrome/browser/sync/util/character_set_converters.h"
+#include "chrome/browser/sync/util/path_helpers.h"
+
+#ifndef OS_LINUX
+#error Compile this file on Linux only.
+#endif
+
+string LastPathSegment(const string& path) {
+ string str(path);
+ string::size_type final_slash = str.find_last_of('/');
+ if (string::npos != final_slash && final_slash == str.length() - 1
+ && str.length() > 1) {
+ str.erase(final_slash);
+ final_slash = str.find_last_of('/');
+ }
+ if (string::npos == final_slash)
+ return str;
+ str.erase(0, final_slash+1);
+ return str;
+}
+
+PathString GetFullPath(const PathString& path) {
+ // TODO(sync): Not sure what the base of the relative path should be on
+ // linux.
+ return path;
+}
+
+PathString AppendSlash(const PathString& path) {
+ if ((!path.empty()) && (*path.rbegin() != '/')) {
+ return path + '/';
+ }
+ return path;
+}
+
+PathString LowercasePath(const PathString& path) {
+ gchar *ret = g_utf8_strdown(path.c_str(), -1);
+ PathString retstr(ret);
+ g_free(ret);
+ return retstr;
+}
diff --git a/chrome/browser/sync/util/path_helpers-posix.cc b/chrome/browser/sync/util/path_helpers-posix.cc
new file mode 100644
index 0000000..02726db
--- /dev/null
+++ b/chrome/browser/sync/util/path_helpers-posix.cc
@@ -0,0 +1,99 @@
+// Copyright (c) 2009 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 <pwd.h>
+
+#include <string.h>
+
+#include "base/port.h"
+#include "chrome/browser/sync/util/path_helpers.h"
+#include "strings/strutil.h"
+
+#if ((!defined(OS_LINUX)) && (!defined(OS_MACOSX)))
+#error Compile this file on Mac OS X or Linux only.
+#endif
+
+PathString ExpandTilde(const PathString& path) {
+ if (path.empty())
+ return path;
+ if ('~' != path[0])
+ return path;
+ PathString ret;
+ // TODO(sync): Consider using getpwuid_r.
+ ret.insert(0, getpwuid(getuid())->pw_dir);
+ ret.append(++path.begin(), path.end());
+ return ret;
+}
+
+namespace {
+// TODO(sync): We really should use char[].
+string cache_dir_;
+}
+
+void set_cache_dir(string cache_dir) {
+ CHECK(cache_dir_.empty());
+ cache_dir_ = cache_dir;
+}
+
+string get_cache_dir() {
+ CHECK(!cache_dir_.empty());
+ return cache_dir_;
+}
+
+// On Posix, PathStrings are UTF-8, not UTF-16 as they are on Windows.
+// Thus, this function is different from the Windows version.
+PathString TruncatePathString(const PathString& original, int length) {
+ if (original.size() <= length)
+ return original;
+ if (length <= 0)
+ return original;
+ PathString ret(original.begin(), original.begin() + length);
+ COMPILE_ASSERT(sizeof(PathChar) == sizeof(uint8), PathStringNotUTF8);
+ PathString::reverse_iterator last_char = ret.rbegin();
+
+ // Values taken from
+ // http://en.wikipedia.org/w/index.php?title=UTF-8&oldid=252875566
+ if (0 == (*last_char & 0x80))
+ return ret;
+
+ for (; last_char != ret.rend(); ++last_char) {
+ if (0 == (*last_char & 0x80)) {
+ // got malformed UTF-8; bail
+ return ret;
+ }
+ if (0 == (*last_char & 0x40)) {
+ // got another trailing byte
+ continue;
+ }
+ break;
+ }
+
+ if (ret.rend() == last_char) {
+ // We hit the beginning of the string. bail.
+ return ret;
+ }
+
+ int last_codepoint_len = last_char - ret.rbegin() + 1;
+
+ if (((0xC0 == (*last_char & 0xE0)) && (2 == last_codepoint_len)) ||
+ ((0xE0 == (*last_char & 0xF0)) && (3 == last_codepoint_len)) ||
+ ((0xF0 == (*last_char & 0xF8)) && (4 == last_codepoint_len))) {
+ // Valid utf-8.
+ return ret;
+ }
+
+ // Invalid utf-8. chop off last "codepoint" and return.
+ ret.resize(ret.size() - last_codepoint_len);
+ return ret;
+}
+
+// Convert /s to :s .
+PathString MakePathComponentOSLegal(const PathString& component) {
+ if (PathString::npos == component.find("/"))
+ return PSTR("");
+ PathString new_name;
+ new_name.reserve(component.size());
+ StringReplace(component, "/", ":", true, &new_name);
+ return new_name;
+}
diff --git a/chrome/browser/sync/util/path_helpers.cc b/chrome/browser/sync/util/path_helpers.cc
new file mode 100644
index 0000000..1cf8d4e
--- /dev/null
+++ b/chrome/browser/sync/util/path_helpers.cc
@@ -0,0 +1,153 @@
+// Copyright (c) 2009 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 "chrome/browser/sync/util/path_helpers.h"
+
+#include <Shlwapi.h>
+#include <stdlib.h>
+
+#include "base/logging.h"
+#include "base/port.h"
+#include "chrome/browser/sync/syncable/syncable.h"
+
+#ifndef OS_WINDOWS
+#error Compile this file on Windows only.
+#endif
+
+using std::string;
+
+#if OS_WINDOWS
+const char PATH_SEPARATOR = '\\';
+#else
+const char PATH_SEPARATOR = '/';
+#endif // OS_WINDOWS
+
+
+static PathString RemoveTrailingSlashes16(PathString str) {
+ while ((str.length() > 0) && (str.at(str.length() - 1) == kPathSeparator[0]))
+ str.resize(str.length() - 1);
+ return str;
+}
+
+static string RemoveTrailingSlashes(string str) {
+ while ((str.length() > 0) && (str.at(str.length() - 1) == PATH_SEPARATOR))
+ str.resize(str.length() - 1);
+ return str;
+}
+
+PathString LastPathSegment(const PathString& path) {
+ return RemoveTrailingSlashes16(PathFindFileNameW(path.c_str()));
+}
+
+string LastPathSegment(const string& path) {
+ return RemoveTrailingSlashes(PathFindFileNameA(path.c_str()));
+}
+
+PathString GetFullPath(const PathString& path) {
+ PathChar buffer[MAX_PATH];
+ PathChar* file_part;
+ DWORD const size = GetFullPathName(path.c_str(), ARRAYSIZE(buffer), buffer,
+ &file_part);
+ return PathString(buffer, size);
+}
+
+PathString AppendSlash(const PathString& path) {
+ PathString result(path);
+ if (!result.empty()) {
+ if (*result.rbegin() == '/')
+ *result.rbegin() = '\\';
+ else if (*result.rbegin() != '\\')
+ result.push_back('\\');
+ }
+ return result;
+}
+
+PathString ExpandTilde(const PathString& path) {
+ // Do nothing on windows.
+ return path;
+}
+
+// Returns a string with length or fewer elements, careful to
+// not truncate a string mid-surrogate pair.
+PathString TruncatePathString(const PathString& original, int length) {
+ if (original.size() <= length)
+ return original;
+ if (length <= 0)
+ return original;
+ PathString ret(original.begin(), original.begin() + length);
+ COMPILE_ASSERT(sizeof(PathChar) == sizeof(uint16), PathStringNotUTF16);
+ PathChar last_char = *ret.rbegin();
+
+ // Values taken from
+ // http://en.wikipedia.org/w/index.php?title=UTF-16/UCS-2&oldid=248072840
+ if (last_char >= 0xD800 && last_char <= 0xDBFF)
+ ret.resize(ret.size() - 1);
+ return ret;
+}
+
+namespace {
+const PathString kWindowsIllegalBaseFilenames[] = {
+ L"CON", L"PRN", L"AUX", L"NUL", L"COM1", L"COM2",
+ L"COM3", L"COM4", L"COM5", L"COM6", L"COM7",
+ L"COM8", L"COM9", L"LPT1", L"LPT2", L"LPT3",
+ L"LPT4", L"LPT5", L"LPT6", L"LPT7", L"LPT8",
+ L"LPT9" };
+}
+
+// See: http://msdn.microsoft.com/library/default.asp?url=/library/
+// en-us/fileio/fs/naming_a_file.asp
+// note that * and ? are not listed on the page as illegal characters,
+// but they are.
+PathString MakePathComponentOSLegal(const PathString& component) {
+ CHECK(!component.empty());
+ PathString mutable_component = component;
+
+ // Remove illegal characters.
+ for (PathString::iterator i = mutable_component.begin();
+ i != mutable_component.end();) {
+ if ((PathString::npos != PathString(L"<>:\"/\\|*?").find(*i)) ||
+ ((static_cast<unsigned short>(*i) >= 0) &&
+ (static_cast<unsigned short>(*i) <= 31))) {
+ mutable_component.erase(i);
+ } else {
+ ++i;
+ }
+ }
+
+ // Remove trailing spaces or periods.
+ while (mutable_component.size() &&
+ ((mutable_component.at(mutable_component.size() - 1) == L' ') ||
+ (mutable_component.at(mutable_component.size() - 1) == L'.')))
+ mutable_component.resize(mutable_component.size() - 1, L' ');
+
+ // Remove a bunch of forbidden names. windows only seems to mind if
+ // a forbidden name matches our name exactly (e.g. "prn") or if the
+ // name is the forbidden name, followed by a dot, followed by anything
+ // (e.g., "prn.anything.foo.bar")
+
+ // From this point out, we break mutable_component into two strings, and
+ // use them this way: we save anything after and including the first dot
+ // (usually the extension) and only mess with stuff before the first dot.
+ PathString::size_type first_dot = mutable_component.find_first_of(L'.');
+ if (PathString::npos == first_dot)
+ first_dot = mutable_component.size();
+ PathString sub = mutable_component.substr(0, first_dot);
+ PathString postsub = mutable_component.substr(first_dot);
+ CHECK(sub + postsub == mutable_component);
+ for (int i = 0; i < ARRAYSIZE(kWindowsIllegalBaseFilenames); i++) {
+ // ComparePathNames(a, b) == 0 -> same
+ if (syncable::ComparePathNames(kWindowsIllegalBaseFilenames[i], sub) == 0) {
+ sub.append(L"~1");
+ break;
+ }
+ }
+ if ((L"" == sub) && (L"" == postsub)) {
+ sub = L"~1";
+ }
+
+ // Return the new name, only if it differs from the original.
+ if ((sub + postsub) == component)
+ return L"";
+ return (sub + postsub);
+}
diff --git a/chrome/browser/sync/util/path_helpers.h b/chrome/browser/sync/util/path_helpers.h
new file mode 100644
index 0000000..d8b4663
--- /dev/null
+++ b/chrome/browser/sync/util/path_helpers.h
@@ -0,0 +1,105 @@
+// Copyright (c) 2009 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 CHROME_BROWSER_SYNC_UTIL_PATH_HELPERS_H_
+#define CHROME_BROWSER_SYNC_UTIL_PATH_HELPERS_H_
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+
+#include "chrome/browser/sync/util/compat-file.h"
+#include "chrome/browser/sync/util/sync_types.h"
+
+template <typename StringType>
+class PathSegmentIterator : public std::iterator<std::forward_iterator_tag,
+ StringType> {
+ public:
+ explicit PathSegmentIterator(const StringType& path) :
+ path_(path), segment_begin_(0), segment_end_(0) {
+ ++(*this);
+ }
+
+ PathSegmentIterator() : segment_begin_(0), segment_end_(0) { }
+
+ // Default copy constructors, constructors, etc. will all do the right thing.
+ PathSegmentIterator& operator ++() {
+ segment_begin_ =
+ std::min(path_.size(),
+ path_.find_first_not_of(kPathSeparator, segment_end_));
+ segment_end_ =
+ std::min(path_.size(),
+ path_.find_first_of(kPathSeparator, segment_begin_));
+ value_.assign(path_, segment_begin_, segment_end_ - segment_begin_);
+ return *this;
+ }
+
+ PathSegmentIterator operator ++(int) {
+ PathSegmentIterator i(*this);
+ return ++i;
+ }
+
+ const StringType& operator * () const {
+ return value_;
+ }
+ const StringType* operator -> () const {
+ return &value_;
+ }
+
+ // If the current value and remaining path are equal, then we
+ // call the iterators equal.
+ bool operator == (const PathSegmentIterator& i) const {
+ return 0 == path_.compare(segment_begin_,
+ path_.size() - segment_begin_,
+ i.path_, i.segment_begin_, i.path_.size() - i.segment_begin_);
+ }
+
+ bool operator != (const PathSegmentIterator& i) const {
+ return !(*this == i);
+ }
+
+ protected:
+ StringType path_;
+ typename StringType::size_type segment_begin_;
+ typename StringType::size_type segment_end_;
+ StringType value_;
+};
+
+// NOTE: The functions (Strip)LastPathSegment always return values without a
+// trailing slash.
+PathString LastPathSegment(const PathString& path);
+std::string LastPathSegment(const std::string& path);
+PathString AppendSlash(const PathString& path);
+PathString GetFullPath(const PathString& path);
+PathString LowercasePath(const PathString& path);
+PathString ExpandTilde(const PathString& path);
+
+inline bool HasSuffixPathString(const PathString& str,
+ const PathString& suffix) {
+ return str.find(suffix, str.size() - suffix.size()) != PathString::npos;
+}
+
+inline PathString StripSuffixPathString(const PathString& str,
+ const PathString& suffix) {
+ PathString ret(str);
+ if (HasSuffixPathString(str, suffix)) {
+ ret.resize(str.size() - suffix.size());
+ }
+ return ret;
+}
+
+// Returns a string with length or fewer elements, careful to
+// not truncate a string mid-surrogate pair.
+PathString TruncatePathString(const PathString& original, int length);
+
+// Makes a path component legal for your OS, but doesn't handle collisions
+// with other files in the same directory. it can do this by removing
+// illegal characters and adding ~1 before the first '.' in the filename.
+// returns PSTR("") if the name is fine as-is
+// on mac/linux we let names stay unicode normalization form C in the system
+// and convert to another normal form in fuse handlers. but, if a '/' is in
+// a filename, we handle it here.
+PathString MakePathComponentOSLegal(const PathString& component);
+
+#endif // CHROME_BROWSER_SYNC_UTIL_PATH_HELPERS_H_
diff --git a/chrome/browser/sync/util/path_helpers_unittest.cc b/chrome/browser/sync/util/path_helpers_unittest.cc
new file mode 100644
index 0000000..75a81a2
--- /dev/null
+++ b/chrome/browser/sync/util/path_helpers_unittest.cc
@@ -0,0 +1,131 @@
+// Copyright (c) 2009 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 "base/logging.h"
+#include "base/port.h"
+#include "chrome/browser/sync/syncable/path_name_cmp.h"
+#include "chrome/browser/sync/util/path_helpers.h"
+#include "chrome/browser/sync/util/sync_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace syncable {
+
+class PathHelpersTest : public testing::Test {
+};
+
+TEST(PathHelpersTest, TruncatePathStringTest) {
+ // Simple case.
+ PathString str = PSTR("12345");
+ EXPECT_EQ(PSTR("123"), TruncatePathString(str, 3));
+ EXPECT_EQ(str, TruncatePathString(str, str.size()));
+
+ // abcg is "abc" + musical g clef U+1D11E
+#if PATHSTRING_IS_STD_STRING
+ // UTF-8
+ PathChar abcg[] = {'a', 'b', 'c', 0xF0, 0x9D, 0x84, 0x9E, '\0'};
+#else // PATHSTRING_IS_STD_STRING
+ // UTF-16
+ PathChar abcg[] = {'a', 'b', 'c', 0xD834, 0xDD1E, '\0'};
+#endif // PATHSTRING_IS_STD_STRING
+
+ EXPECT_EQ(PSTR("abc"), TruncatePathString(abcg, 4));
+
+ // Further utf-8 tests.
+#if PATHSTRING_IS_STD_STRING
+ // UTF-8
+
+ EXPECT_EQ(PSTR("abc"), TruncatePathString(abcg, 4));
+ EXPECT_EQ(PSTR("abc"), TruncatePathString(abcg, 5));
+ EXPECT_EQ(PSTR("abc"), TruncatePathString(abcg, 6));
+ EXPECT_EQ(PathString(abcg), TruncatePathString(abcg, 7));
+
+ PathChar abc2[] = {'a', 'b', 'c', 0xC3, 0xB1, '\0'}; // abc(n w/ tilde)
+ EXPECT_EQ(PSTR("abc"), TruncatePathString(abc2, 3));
+ EXPECT_EQ(PSTR("abc"), TruncatePathString(abc2, 4));
+ EXPECT_EQ(PathString(abc2), TruncatePathString(abc2, 5));
+
+ PathChar abc3[] = {'a', 'b', 'c', 0xE2, 0x82, 0xAC, '\0'}; // abc(euro)
+ EXPECT_EQ(PSTR("abc"), TruncatePathString(abc3, 3));
+ EXPECT_EQ(PSTR("abc"), TruncatePathString(abc3, 4));
+ EXPECT_EQ(PSTR("abc"), TruncatePathString(abc3, 5));
+ EXPECT_EQ(PathString(abc3), TruncatePathString(abc3, 6));
+#endif
+}
+
+TEST(PathHelpersTest, PathStrutil) {
+ PathString big = PSTR("abcdef");
+ PathString suffix = PSTR("def");
+ PathString other = PSTR("x");
+ EXPECT_TRUE(HasSuffixPathString(big, suffix));
+ EXPECT_FALSE(HasSuffixPathString(suffix, big));
+ EXPECT_FALSE(HasSuffixPathString(big, other));
+ EXPECT_EQ(PSTR("abc"), StripSuffixPathString(big, suffix));
+}
+
+TEST(PathHelpersTest, SanitizePathComponent) {
+#ifdef OS_WINDOWS
+ EXPECT_EQ(MakePathComponentOSLegal(L"bar"), L"");
+ EXPECT_EQ(MakePathComponentOSLegal(L"bar <"), L"bar");
+ EXPECT_EQ(MakePathComponentOSLegal(L"bar.<"), L"bar");
+ EXPECT_EQ(MakePathComponentOSLegal(L"prn"), L"prn~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"pr>n"), L"prn~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"ab:c"), L"abc");
+ EXPECT_EQ(MakePathComponentOSLegal(L"a|bc"), L"abc");
+ EXPECT_EQ(MakePathComponentOSLegal(L"baz~9"), L"");
+ EXPECT_EQ(MakePathComponentOSLegal(L"\007"), L"~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"com1.txt.bat"), L"com1~1.txt.bat");
+ EXPECT_EQ(MakePathComponentOSLegal(L"foo.com1.bat"), L"");
+ EXPECT_EQ(MakePathComponentOSLegal(L"\010gg"), L"gg");
+ EXPECT_EQ(MakePathComponentOSLegal(L"<"), L"~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"col:on"), L"colon");
+ EXPECT_EQ(MakePathComponentOSLegal(L"q\""), L"q");
+ EXPECT_EQ(MakePathComponentOSLegal(L"back\\slAsh"), L"backslAsh");
+ EXPECT_EQ(MakePathComponentOSLegal(L"sla/sh "), L"slash");
+ EXPECT_EQ(MakePathComponentOSLegal(L"s|laSh"), L"slaSh");
+ EXPECT_EQ(MakePathComponentOSLegal(L"CON"), L"CON~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"PRN"), L"PRN~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"AUX"), L"AUX~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"NUL"), L"NUL~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"COM1"), L"COM1~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"COM2"), L"COM2~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"COM3"), L"COM3~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"COM4"), L"COM4~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"COM5"), L"COM5~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"COM6"), L"COM6~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"COM7"), L"COM7~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"COM8"), L"COM8~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"COM9"), L"COM9~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"LPT1"), L"LPT1~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"LPT2"), L"LPT2~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"LPT3"), L"LPT3~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"LPT4"), L"LPT4~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"LPT5"), L"LPT5~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"LPT6"), L"LPT6~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"LPT7"), L"LPT7~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"LPT8"), L"LPT8~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"LPT9"), L"LPT9~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"bar~bar"), L"");
+ EXPECT_EQ(MakePathComponentOSLegal(L"adlr~-3"), L"");
+ EXPECT_EQ(MakePathComponentOSLegal(L"tilde~"), L"");
+ EXPECT_EQ(MakePathComponentOSLegal(L"mytext.txt"), L"");
+ EXPECT_EQ(MakePathComponentOSLegal(L"mytext|.txt"), L"mytext.txt");
+ EXPECT_EQ(MakePathComponentOSLegal(L"okay.com1.txt"), L"");
+ EXPECT_EQ(MakePathComponentOSLegal(L"software-3.tar.gz"), L"");
+ EXPECT_EQ(MakePathComponentOSLegal(L"<"), L"~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"<.<"), L"~1");
+ EXPECT_EQ(MakePathComponentOSLegal(L"<.<txt"), L".txt");
+ EXPECT_EQ(MakePathComponentOSLegal(L"txt<.<"), L"txt");
+#else // OS_WINDOWS
+
+ EXPECT_EQ(MakePathComponentOSLegal("bar"), "");
+ EXPECT_EQ(MakePathComponentOSLegal("b"), "");
+ EXPECT_EQ(MakePathComponentOSLegal("A"), "");
+ EXPECT_EQ(MakePathComponentOSLegal("<'|"), "");
+ EXPECT_EQ(MakePathComponentOSLegal("/"), ":");
+ EXPECT_EQ(MakePathComponentOSLegal(":"), "");
+
+#endif // OS_WINDOWS
+}
+
+} // namespace syncable
diff --git a/chrome/browser/sync/util/pthread_helpers.cc b/chrome/browser/sync/util/pthread_helpers.cc
new file mode 100644
index 0000000..4dadc55
--- /dev/null
+++ b/chrome/browser/sync/util/pthread_helpers.cc
@@ -0,0 +1,162 @@
+// Copyright (c) 2009 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 "chrome/browser/sync/util/pthread_helpers.h"
+
+#if (defined(OS_LINUX) || defined(OS_MACOSX))
+#include <sys/time.h>
+#endif // (defined(OS_LINUX) || defined(OS_MACOSX))
+
+#include "base/atomicops.h"
+#include "base/logging.h"
+#include "base/port.h"
+#include "base/scoped_ptr.h"
+#include "chrome/browser/sync/protocol/service_constants.h"
+
+#ifdef OS_WINDOWS
+
+namespace {
+
+// Ensure that we don't bug the user more than once about the process being
+// terminated.
+base::subtle::AtomicWord g_process_terminating = 0;
+
+struct ThreadStartParams {
+ void *(*start) (void* payload);
+ void* param;
+};
+
+void* ThreadMainProc(void* parameter) {
+ ThreadStartParams* tsp = reinterpret_cast<ThreadStartParams*>(parameter);
+ void *(*start) (void *) = tsp->start;
+ void* param = tsp->param;
+
+ delete tsp;
+
+ void* result = NULL;
+ __try {
+ result = start(param);
+ } __except(EXCEPTION_CONTINUE_SEARCH) {
+ // Make sure only one thread complains and exits the process. Other
+ // faulting threads simply return.
+ if (0 == base::subtle::NoBarrier_CompareAndSwap(
+ &g_process_terminating, 0, 1)) {
+ // Service notification means we don't have a recursive event loop inside
+ // this call, and so won't suffer recursive exceptions.
+ ::MessageBox(NULL,
+ PRODUCT_NAME_STRING
+ L" has suffered a non-recoverable\n"
+ L"exception, and must exit immediately",
+ L"Nonrecoverable Exception",
+ MB_OK | MB_APPLMODAL | MB_SERVICE_NOTIFICATION);
+
+ ::ExitProcess(GetExceptionCode());
+ }
+ }
+
+ return result;
+}
+
+} // namespace
+
+#endif
+
+thread_handle CreatePThread(void *(*start) (void *), void* parameter) {
+#ifdef OS_WINDOWS
+ scoped_ptr<ThreadStartParams> param(new ThreadStartParams);
+ if (NULL == param.get())
+ return NULL;
+
+ param->start = start;
+ param->param = parameter;
+
+ pthread_t pthread;
+ if (0 != pthread_create(&pthread, NULL, ThreadMainProc, param.get()))
+ return NULL;
+
+ // ownership has passed to the new thread
+ param.release();
+
+ const HANDLE thread_handle = pthread_getw32threadhandle_np(pthread);
+ HANDLE thread_copy;
+ // Have to duplicate the thread handle, because when we call
+ // pthread_detach(), the handle will get closed as soon as the thread exits.
+ // We want to keep the handle indefinitely.
+ CHECK(DuplicateHandle(GetCurrentProcess(), thread_handle,
+ GetCurrentProcess(), &thread_copy, 0, FALSE,
+ DUPLICATE_SAME_ACCESS)) <<
+ "DuplicateHandle() failed with error " << GetLastError();
+ pthread_detach(pthread);
+ return thread_copy;
+#else
+ pthread_t handle;
+
+ int result = pthread_create(&handle, NULL, start, parameter);
+ if (result == 0) {
+ return handle;
+ } else {
+ return 0;
+ }
+#endif
+}
+
+struct timespec GetPThreadAbsoluteTime(uint32 ms) {
+#ifdef OS_WINDOWS
+ FILETIME filenow;
+ GetSystemTimeAsFileTime(&filenow);
+ ULARGE_INTEGER n;
+ n.LowPart = filenow.dwLowDateTime;
+ n.HighPart = filenow.dwHighDateTime;
+ // Filetime unit is 100-nanosecond intervals
+ const int64 ms_ftime = 10000;
+ n.QuadPart += ms_ftime * ms;
+
+ // The number of 100 nanosecond intervals from Jan 1, 1601 'til Jan 1, 1970.
+ const int64 kOffset = GG_LONGLONG(116444736000000000);
+ timespec result;
+ result.tv_sec = (n.QuadPart - kOffset) / 10000000;
+ result.tv_nsec = (n.QuadPart - kOffset -
+ (result.tv_sec * GG_LONGLONG(10000000))) * 100;
+ return result;
+#else
+ struct timeval now;
+ struct timezone zone;
+ gettimeofday(&now, &zone);
+ struct timespec deadline = { now.tv_sec };
+ // microseconds to nanoseconds.
+ // and add the ms delay.
+ ms += now.tv_usec / 1000;
+ deadline.tv_sec += ms / 1000;
+ deadline.tv_nsec = (ms % 1000) * 1000000;
+ return deadline;
+#endif
+}
+
+void NameCurrentThreadForDebugging(char* name) {
+#if defined(OS_WINDOWS)
+ // This implementation is taken from Chromium's platform_thread framework.
+ // The information on how to set the thread name comes from a MSDN article:
+ // http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx
+ const DWORD kVCThreadNameException = 0x406D1388;
+ typedef struct tagTHREADNAME_INFO {
+ DWORD dwType; // Must be 0x1000.
+ LPCSTR szName; // Pointer to name (in user addr space).
+ DWORD dwThreadID; // Thread ID (-1=caller thread).
+ DWORD dwFlags; // Reserved for future use, must be zero.
+ } THREADNAME_INFO;
+
+ // The debugger needs to be around to catch the name in the exception. If
+ // there isn't a debugger, we are just needlessly throwing an exception.
+ if (!::IsDebuggerPresent())
+ return;
+
+ THREADNAME_INFO info = { 0x1000, name, GetCurrentThreadId(), 0 };
+
+ __try {
+ RaiseException(kVCThreadNameException, 0, sizeof(info)/sizeof(DWORD),
+ reinterpret_cast<DWORD_PTR*>(&info));
+ } __except(EXCEPTION_CONTINUE_EXECUTION) {
+ }
+#endif // defined(OS_WINDOWS)
+}
diff --git a/chrome/browser/sync/util/pthread_helpers.h b/chrome/browser/sync/util/pthread_helpers.h
new file mode 100644
index 0000000..26defe0
--- /dev/null
+++ b/chrome/browser/sync/util/pthread_helpers.h
@@ -0,0 +1,259 @@
+// Copyright (c) 2009 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 CHROME_BROWSER_SYNC_UTIL_PTHREAD_HELPERS_H_
+#define CHROME_BROWSER_SYNC_UTIL_PTHREAD_HELPERS_H_
+
+#include <pthread.h>
+#include "base/logging.h"
+
+#ifdef OS_WINDOWS
+typedef void* thread_handle;
+#else
+typedef pthread_t thread_handle;
+#endif
+
+// Creates a pthread, detaches from it, and returns a Win32 HANDLE for it that
+// the caller must CloseHandle().
+thread_handle CreatePThread(void* (*start)(void* payload), void* parameter);
+
+class PThreadRWLock {
+ public:
+ inline PThreadRWLock() {
+ int result = pthread_rwlock_init(&rwlock_, 0);
+ DCHECK_EQ(0, result);
+ }
+ ~PThreadRWLock() {
+ int result = pthread_rwlock_destroy(&rwlock_);
+ DCHECK_EQ(0, result);
+ }
+ pthread_rwlock_t rwlock_;
+
+ DISALLOW_COPY_AND_ASSIGN(PThreadRWLock);
+};
+
+// ScopedReadLock attempts to acquire a write lock in its constructor and then
+// releases it in its destructor.
+class ScopedWriteLock {
+ public:
+ explicit ScopedWriteLock(pthread_rwlock_t* rwlock) : rwlock_(rwlock) {
+ int result = pthread_rwlock_wrlock(rwlock_);
+ DCHECK_EQ(0, result);
+ }
+
+ explicit ScopedWriteLock(PThreadRWLock* rwlock) : rwlock_(&rwlock->rwlock_) {
+ int result = pthread_rwlock_wrlock(rwlock_);
+ DCHECK_EQ(0, result);
+ }
+
+ ~ScopedWriteLock() {
+ int result = pthread_rwlock_unlock(rwlock_);
+ DCHECK_EQ(0, result);
+ }
+
+ protected:
+ pthread_rwlock_t* rwlock_;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedWriteLock);
+};
+
+// ScopedReadLock attempts to acquire a read lock in its constructor and then
+// releases it in its destructor.
+class ScopedReadLock {
+ public:
+ explicit ScopedReadLock(pthread_rwlock_t* rwlock) : rwlock_(rwlock) {
+ int result = pthread_rwlock_rdlock(rwlock_);
+ DCHECK_EQ(0, result);
+ }
+
+ explicit ScopedReadLock(PThreadRWLock* rwlock) : rwlock_(&rwlock->rwlock_) {
+ int result = pthread_rwlock_rdlock(rwlock_);
+ DCHECK_EQ(0, result);
+ }
+
+ ~ScopedReadLock() {
+ int result = pthread_rwlock_unlock(rwlock_);
+ DCHECK_EQ(0, result);
+ }
+ protected:
+ pthread_rwlock_t* rwlock_;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedReadLock);
+};
+
+template <typename LockType>
+class PThreadScopedLock {
+ public:
+ explicit inline PThreadScopedLock(LockType* lock) : lock_(lock) {
+ if (lock_)
+ lock->Lock();
+ }
+ inline ~PThreadScopedLock() {
+ Unlock();
+ }
+ inline void Unlock() {
+ if (lock_) {
+ lock_->Unlock();
+ lock_ = NULL;
+ }
+ }
+ LockType* lock_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PThreadScopedLock);
+};
+
+class PThreadNoLock {
+ public:
+ inline void Lock() { }
+ inline void Unlock() { }
+};
+
+// On win32, the pthread mutex implementation is about as efficient a critical
+// section. It uses atomic operations and only needs kernel calls on
+// contention.
+class PThreadMutex {
+ public:
+ inline PThreadMutex() {
+ pthread_mutexattr_t* attributes = NULL;
+#ifndef NDEBUG
+ private_attributes_in_use_ = true;
+ pthread_mutexattr_init(&mutex_attributes_);
+ pthread_mutexattr_settype(&mutex_attributes_, PTHREAD_MUTEX_ERRORCHECK);
+ attributes = &mutex_attributes_;
+#endif
+ int result = pthread_mutex_init(&mutex_, attributes);
+ DCHECK_EQ(0, result);
+ }
+ inline explicit PThreadMutex(const pthread_mutexattr_t* attr) {
+#ifndef NDEBUG
+ private_attributes_in_use_ = false;
+#endif
+ int result = pthread_mutex_init(&mutex_, attr);
+ DCHECK_EQ(0, result);
+ }
+ inline ~PThreadMutex() {
+ int result = pthread_mutex_destroy(&mutex_);
+ DCHECK_EQ(0, result);
+#ifndef NDEBUG
+ if (private_attributes_in_use_) {
+ pthread_mutexattr_destroy(&mutex_attributes_);
+ }
+#endif
+ }
+ inline void Lock() {
+ int result = pthread_mutex_lock(&mutex_);
+ DCHECK_EQ(0, result);
+ }
+ inline void Unlock() {
+ int result = pthread_mutex_unlock(&mutex_);
+ DCHECK_EQ(0, result);
+ }
+ pthread_mutex_t mutex_;
+
+#ifndef NDEBUG
+ pthread_mutexattr_t mutex_attributes_;
+ bool private_attributes_in_use_;
+#endif
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PThreadMutex);
+};
+
+class PThreadMutexAttr {
+ public:
+ pthread_mutexattr_t attr;
+ inline PThreadMutexAttr(int type) {
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, type);
+ }
+ inline ~PThreadMutexAttr() {
+ pthread_mutexattr_destroy(&attr);
+ }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PThreadMutexAttr);
+};
+
+class PThreadRecursiveMutex : public PThreadMutex {
+ public:
+ inline PThreadRecursiveMutex() : PThreadMutex(recursive_attr()) {}
+ private:
+ static inline pthread_mutexattr_t* recursive_attr() {
+ static PThreadMutexAttr recursive(PTHREAD_MUTEX_RECURSIVE);
+ return &recursive.attr;
+ }
+ DISALLOW_COPY_AND_ASSIGN(PThreadRecursiveMutex);
+};
+
+
+class PThreadScopedDisabledCancellation {
+ public:
+ inline PThreadScopedDisabledCancellation() {
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancel_state_);
+ }
+ inline ~PThreadScopedDisabledCancellation() {
+ pthread_setcancelstate(old_cancel_state_, NULL);
+ }
+ private:
+ int old_cancel_state_;
+
+ DISALLOW_COPY_AND_ASSIGN(PThreadScopedDisabledCancellation);
+};
+
+class PThreadCondVar {
+ public:
+ inline PThreadCondVar() {
+ int result = pthread_cond_init(&condvar_, 0);
+ DCHECK_EQ(0, result);
+ }
+ ~PThreadCondVar() {
+ int result = pthread_cond_destroy(&condvar_);
+ DCHECK_EQ(0, result);
+ }
+ inline void Signal() {
+ int result = pthread_cond_signal(&condvar_);
+ DCHECK_EQ(0, result);
+ }
+ inline void Broadcast() {
+ int result = pthread_cond_broadcast(&condvar_);
+ DCHECK_EQ(0, result);
+ }
+ pthread_cond_t condvar_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PThreadCondVar);
+};
+
+template <typename ValueType>
+class PThreadThreadVar {
+ public:
+ PThreadThreadVar(void (*destructor)(void* payload)) {
+ int result = pthread_key_create(&thread_key_, destructor);
+ DCHECK_EQ(0, result);
+ }
+ ~PThreadThreadVar() {
+ int result = pthread_key_delete(thread_key_);
+ DCHECK_EQ(0, result);
+ }
+ void SetValue(ValueType value) {
+ int result = pthread_setspecific(thread_key_, static_cast<void*>(value));
+ DCHECK_EQ(0, result);
+ }
+ ValueType GetValue() {
+ return static_cast<ValueType>(pthread_getspecific(thread_key_));
+ }
+ private:
+ pthread_key_t thread_key_;
+ DISALLOW_COPY_AND_ASSIGN(PThreadThreadVar);
+};
+
+// Returns the absolute time ms milliseconds from now. Useful for passing
+// result to pthread_cond_timedwait().
+struct timespec GetPThreadAbsoluteTime(uint32 ms_from_now);
+
+// Assign a descriptive label to the current thread. This is useful to see
+// in a GUI debugger, but may not be implementable on all platforms.
+void NameCurrentThreadForDebugging(char* name);
+
+#endif // CHROME_BROWSER_SYNC_UTIL_PTHREAD_HELPERS_H_
diff --git a/chrome/browser/sync/util/pthread_helpers_fwd.h b/chrome/browser/sync/util/pthread_helpers_fwd.h
new file mode 100644
index 0000000..2756fceb
--- /dev/null
+++ b/chrome/browser/sync/util/pthread_helpers_fwd.h
@@ -0,0 +1,13 @@
+// Copyright (c) 2009 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 CHROME_BROWSER_SYNC_UTIL_PTHREAD_HELPERS_FWD_H_
+#define CHROME_BROWSER_SYNC_UTIL_PTHREAD_HELPERS_FWD_H_
+
+template <typename LockType>
+class PThreadScopedLock;
+class PThreadNoLock;
+class PThreadMutex;
+
+#endif // CHROME_BROWSER_SYNC_UTIL_PTHREAD_HELPERS_FWD_H_
diff --git a/chrome/browser/sync/util/query_helpers.cc b/chrome/browser/sync/util/query_helpers.cc
new file mode 100644
index 0000000..e640a6c
--- /dev/null
+++ b/chrome/browser/sync/util/query_helpers.cc
@@ -0,0 +1,282 @@
+// Copyright (c) 2009 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 "chrome/browser/sync/util/query_helpers.h"
+
+#if defined(OS_WINDOWS)
+#include <windows.h>
+#endif
+
+#include <limits>
+#include <string>
+#include <vector>
+
+#include "chrome/browser/sync/util/sync_types.h"
+
+using std::numeric_limits;
+using std::string;
+using std::vector;
+
+sqlite3_stmt* PrepareQuery(sqlite3* dbhandle, const char* query) {
+ sqlite3_stmt* statement = NULL;
+ const char* query_tail;
+ if (SQLITE_OK != sqlite3_prepare(dbhandle, query,
+ CountBytes(query), &statement,
+ &query_tail)) {
+ LOG(ERROR) << query << "\n" << sqlite3_errmsg(dbhandle);
+ }
+ return statement;
+}
+
+void ExecOrDie(sqlite3* dbhandle, const char* query) {
+ return ExecOrDie(dbhandle, query, PrepareQuery(dbhandle, query));
+}
+
+// Finalizes (deletes) the query before returning.
+void ExecOrDie(sqlite3* dbhandle, const char* query, sqlite3_stmt* statement) {
+ int result = Exec(dbhandle, query, statement);
+ if (SQLITE_DONE != result) {
+ LOG(FATAL) << query << "\n" << sqlite3_errmsg(dbhandle);
+ }
+}
+
+int Exec(sqlite3* dbhandle, const char* query) {
+ return Exec(dbhandle, query, PrepareQuery(dbhandle, query));
+}
+
+// Finalizes (deletes) the query before returning.
+int Exec(sqlite3* dbhandle, const char* query, sqlite3_stmt* statement) {
+ int result;
+ do {
+ result = sqlite3_step(statement);
+ } while (SQLITE_ROW == result);
+ int finalize_result = sqlite3_finalize(statement);
+ return SQLITE_OK == finalize_result ? result : finalize_result;
+}
+
+int SqliteOpen(PathString filename, sqlite3** db) {
+ int result =
+#if PATHSTRING_IS_STD_STRING
+ sqlite3_open
+#else
+ sqlite3_open16
+#endif
+ (filename.c_str(), db);
+ LOG_IF(ERROR, SQLITE_OK != result) << "Error opening " << filename << ": "
+ << result;
+#ifdef OS_WINDOWS
+ if (SQLITE_OK == result) {
+ // Make sure we mark the db file as not indexed so since if any other app
+ // opens it, it can break our db locking.
+ DWORD attrs = GetFileAttributes(filename.c_str());
+ if (FILE_ATTRIBUTE_NORMAL == attrs)
+ attrs = FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
+ else
+ attrs = attrs | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
+ SetFileAttributes(filename.c_str(), attrs);
+ }
+#endif
+ // Be patient as we set pragmas.
+ sqlite3_busy_timeout(*db, numeric_limits<int>::max());
+#ifndef DISABLE_SQLITE_FULL_FSYNC
+ ExecOrDie(*db, "PRAGMA fullfsync = 1");
+#endif // DISABLE_SQLITE_FULL_FSYNC
+ ExecOrDie(*db, "PRAGMA synchronous = 2");
+ sqlite3_busy_timeout(*db, 0);
+ return SQLITE_OK;
+}
+
+#if !PATHSTRING_IS_STD_STRING
+sqlite3_stmt* BindArg(sqlite3_stmt* statement, const PathString& s, int index) {
+ if (NULL == statement)
+ return statement;
+ CHECK(SQLITE_OK == sqlite3_bind_text16(statement, index, s.data(),
+ CountBytes(s), SQLITE_TRANSIENT));
+ return statement;
+}
+
+sqlite3_stmt* BindArg(sqlite3_stmt* statement, const PathChar* s, int index) {
+ if (NULL == statement)
+ return statement;
+ CHECK(SQLITE_OK == sqlite3_bind_text16(statement,
+ index,
+ s,
+ -1, // -1 means s is zero-terminated
+ SQLITE_TRANSIENT));
+ return statement;
+}
+#endif
+
+sqlite3_stmt* BindArg(sqlite3_stmt* statement, const string& s, int index) {
+ if (NULL == statement)
+ return statement;
+ CHECK(SQLITE_OK == sqlite3_bind_text(statement,
+ index,
+ s.data(),
+ CountBytes(s),
+ SQLITE_TRANSIENT));
+ return statement;
+}
+
+sqlite3_stmt* BindArg(sqlite3_stmt* statement, const char* s, int index) {
+ if (NULL == statement)
+ return statement;
+ CHECK(SQLITE_OK == sqlite3_bind_text(statement,
+ index,
+ s,
+ -1, // -1 means s is zero-terminated
+ SQLITE_TRANSIENT));
+ return statement;
+}
+
+sqlite3_stmt* BindArg(sqlite3_stmt* statement, int32 n, int index) {
+ if (NULL == statement)
+ return statement;
+ CHECK(SQLITE_OK == sqlite3_bind_int(statement, index, n));
+ return statement;
+}
+
+sqlite3_stmt* BindArg(sqlite3_stmt* statement, int64 n, int index) {
+ if (NULL == statement)
+ return statement;
+ CHECK(SQLITE_OK == sqlite3_bind_int64(statement, index, n));
+ return statement;
+}
+
+sqlite3_stmt* BindArg(sqlite3_stmt* statement, double n, int index) {
+ if (NULL == statement)
+ return statement;
+ CHECK(SQLITE_OK == sqlite3_bind_double(statement, index, n));
+ return statement;
+}
+
+sqlite3_stmt* BindArg(sqlite3_stmt* statement, bool b, int index) {
+ if (NULL == statement)
+ return statement;
+ int32 n = b ? 1 : 0;
+ CHECK(SQLITE_OK == sqlite3_bind_int(statement, index, n));
+ return statement;
+}
+
+sqlite3_stmt* BindArg(sqlite3_stmt* statement, const vector<uint8>& v,
+ int index) {
+ if (NULL == statement)
+ return statement;
+ uint8* blob = v.empty() ? NULL : const_cast<uint8*>(&v[0]);
+ CHECK(SQLITE_OK == sqlite3_bind_blob(statement,
+ index,
+ blob,
+ v.size(),
+ SQLITE_TRANSIENT));
+ return statement;
+}
+
+sqlite3_stmt* BindArg(sqlite3_stmt* statement, SqliteNullType, int index) {
+ if (NULL == statement)
+ return statement;
+ CHECK(SQLITE_OK == sqlite3_bind_null(statement, index));
+ return statement;
+}
+
+
+#if !PATHSTRING_IS_STD_STRING
+void GetColumn(sqlite3_stmt* statement, int index, PathString* value) {
+ if (sqlite3_column_type(statement, index) == SQLITE_NULL) {
+ value->clear();
+ } else {
+ value->assign(
+ static_cast<const PathChar*>(sqlite3_column_text16(statement, index)),
+ sqlite3_column_bytes16(statement, index) / sizeof(PathChar));
+ }
+}
+#endif
+
+void GetColumn(sqlite3_stmt* statement, int index, string* value) {
+ if (sqlite3_column_type(statement, index) == SQLITE_NULL) {
+ value->clear();
+ } else {
+ value->assign(
+ reinterpret_cast<const char*>(sqlite3_column_text(statement, index)),
+ sqlite3_column_bytes(statement, index));
+ }
+}
+
+void GetColumn(sqlite3_stmt* statement, int index, int32* value) {
+ *value = sqlite3_column_int(statement, index);
+}
+
+void GetColumn(sqlite3_stmt* statement, int index, int64* value) {
+ *value = sqlite3_column_int64(statement, index);
+}
+
+void GetColumn(sqlite3_stmt* statement, int index, double* value) {
+ *value = sqlite3_column_double(statement, index);
+}
+
+void GetColumn(sqlite3_stmt* statement, int index, bool* value) {
+ *value = (0 != sqlite3_column_int(statement, index));
+}
+
+void GetColumn(sqlite3_stmt* statement, int index, std::vector<uint8>* value) {
+ if (sqlite3_column_type(statement, index) == SQLITE_NULL) {
+ value->clear();
+ } else {
+ const uint8* blob =
+ reinterpret_cast<const uint8*>(sqlite3_column_blob(statement, index));
+ for (int i = 0; i < sqlite3_column_bytes(statement, index); i++)
+ value->push_back(blob[i]);
+ }
+}
+
+bool DoesTableExist(sqlite3 *dbhandle, const string &table_name) {
+ ScopedStatement count_query
+ (PrepareQuery(dbhandle,
+ "SELECT count(*) from sqlite_master where name = ?",
+ table_name));
+
+ int query_result = sqlite3_step(count_query.get());
+ CHECK(SQLITE_ROW == query_result);
+ int count = sqlite3_column_int(count_query.get(), 0);
+
+ return 1 == count;
+}
+
+void ScopedStatement::reset(sqlite3_stmt* statement) {
+ if (NULL != statement_)
+ sqlite3_finalize(statement_);
+ statement_ = statement;
+}
+
+ScopedStatement::~ScopedStatement() {
+ reset(NULL);
+}
+
+ScopedStatementResetter::~ScopedStatementResetter() {
+ sqlite3_reset(statement_);
+}
+
+// Useful for encoding any sequence of bytes into a string that can be used in
+// a table name. Kind of like hex encoding, except that A is zero and P is 15.
+string APEncode(const string& in) {
+ string result;
+ result.reserve(in.size() * 2);
+ for (string::const_iterator i = in.begin(); i != in.end(); ++i) {
+ unsigned int c = static_cast<unsigned char>(*i);
+ result.push_back((c & 0x0F) + 'A');
+ result.push_back(((c >> 4) & 0x0F) + 'A');
+ }
+ return result;
+}
+
+string APDecode(const string& in) {
+ string result;
+ result.reserve(in.size() / 2);
+ for (string::const_iterator i = in.begin(); i != in.end(); ++i) {
+ unsigned int c = *i - 'A';
+ if (++i != in.end())
+ c = c | (static_cast<unsigned char>(*i - 'A') << 4);
+ result.push_back(c);
+ }
+ return result;
+}
diff --git a/chrome/browser/sync/util/query_helpers.h b/chrome/browser/sync/util/query_helpers.h
new file mode 100644
index 0000000..73aa422
--- /dev/null
+++ b/chrome/browser/sync/util/query_helpers.h
@@ -0,0 +1,698 @@
+// Copyright (c) 2009 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.
+//
+// Typesafe composition of SQL query strings.
+
+#ifndef CHROME_BROWSER_SYNC_UTIL_QUERY_HELPERS_H_
+#define CHROME_BROWSER_SYNC_UTIL_QUERY_HELPERS_H_
+
+#include <limits>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "chrome/browser/sync/util/sync_types.h"
+#include "third_party/sqlite/preprocessed/sqlite3.h"
+
+// Sometimes threads contend on the DB lock itself, especially when one thread
+// is calling SaveChanges. In the worst case scenario, the user can put his
+// laptop to sleep during db contention, and wake up the laptop days later, so
+// infinity seems like the best choice here.
+const int kDirectoryBackingStoreBusyTimeoutMs = std::numeric_limits<int>::max();
+
+enum SqliteNullType {
+ SQLITE_NULL_VALUE
+};
+
+int SqliteOpen(PathString filename, sqlite3** ppDb);
+
+sqlite3_stmt* PrepareQuery(sqlite3* dbhandle, const char* query);
+#if !PATHSTRING_IS_STD_STRING
+sqlite3_stmt* BindArg(sqlite3_stmt*, const PathString&, int index);
+sqlite3_stmt* BindArg(sqlite3_stmt*, const PathChar*, int index);
+#endif
+sqlite3_stmt* BindArg(sqlite3_stmt*, const std::string&, int index);
+sqlite3_stmt* BindArg(sqlite3_stmt*, const char*, int index);
+sqlite3_stmt* BindArg(sqlite3_stmt*, int32, int index);
+sqlite3_stmt* BindArg(sqlite3_stmt*, int64, int index);
+sqlite3_stmt* BindArg(sqlite3_stmt*, double, int index);
+sqlite3_stmt* BindArg(sqlite3_stmt*, bool, int index);
+sqlite3_stmt* BindArg(sqlite3_stmt*, const std::vector<uint8>&, int index);
+sqlite3_stmt* BindArg(sqlite3_stmt*, SqliteNullType, int index);
+
+#if !PATHSTRING_IS_STD_STRING
+void GetColumn(sqlite3_stmt*, int index, PathString* value);
+#endif
+void GetColumn(sqlite3_stmt*, int index, std::string* value);
+void GetColumn(sqlite3_stmt*, int index, int32* value);
+void GetColumn(sqlite3_stmt*, int index, int64* value);
+void GetColumn(sqlite3_stmt*, int index, double* value);
+void GetColumn(sqlite3_stmt*, int index, bool* value);
+void GetColumn(sqlite3_stmt*, int index, std::vector<uint8>* value);
+
+bool DoesTableExist(sqlite3* dbhandle, const std::string& tablename);
+
+// Prepares a query with a WHERE clause that filters the values by the items
+// passed inside of the Vector.
+// Example:
+//
+// vector<PathString> v;
+// v.push_back("abc");
+// v.push_back("123");
+// PrepareQuery(dbhandle, "SELECT * FROM table", "column_name", v.begin(),
+// v.end(), "ORDER BY id");
+//
+// will produce the following query.
+//
+// SELECT * FROM table WHERE column_name = 'abc' OR column_name = '123' ORDER BY
+// id.
+//
+template<typename ItemIterator>
+sqlite3_stmt* PrepareQueryWhereColumnIn(sqlite3* dbhandle,
+ const std::string& query_head,
+ const std::string& filtername,
+ ItemIterator begin, ItemIterator end,
+ const std::string& query_options) {
+ std::string query;
+ query.reserve(512);
+ query += query_head;
+ const char* joiner = " WHERE ";
+ for (ItemIterator it = begin; it != end; ++it) {
+ query += joiner;
+ query += filtername;
+ query += " = ?";
+ joiner = " OR ";
+ }
+ query += " ";
+ query += query_options;
+ sqlite3_stmt* statement = NULL;
+ const char* query_tail;
+ if (SQLITE_OK != sqlite3_prepare(dbhandle, query.data(),
+ CountBytes(query), &statement,
+ &query_tail)) {
+ LOG(ERROR) << query << "\n" << sqlite3_errmsg(dbhandle);
+ }
+ int index = 1;
+ for (ItemIterator it = begin; it != end; ++it) {
+ BindArg(statement, *it, index);
+ ++index;
+ }
+ return statement;
+}
+
+template <typename Type1>
+inline sqlite3_stmt* PrepareQuery(sqlite3* dbhandle, const char* query,
+ const Type1& arg1) {
+ return BindArg(PrepareQuery(dbhandle, query), arg1, 1);
+}
+
+template <typename Type1, typename Type2>
+inline sqlite3_stmt* PrepareQuery(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2) {
+ return BindArg(PrepareQuery(dbhandle, query, arg1), arg2, 2);
+}
+
+template <typename Type1, typename Type2, typename Type3>
+inline sqlite3_stmt* PrepareQuery(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3) {
+ return BindArg(PrepareQuery(dbhandle, query, arg1, arg2), arg3, 3);
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4>
+inline sqlite3_stmt* PrepareQuery(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4) {
+ return BindArg(PrepareQuery(dbhandle, query, arg1, arg2, arg3), arg4, 4);
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5>
+inline sqlite3_stmt* PrepareQuery(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5) {
+ return BindArg(PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4),
+ arg5, 5);
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6>
+inline sqlite3_stmt* PrepareQuery(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6) {
+ return BindArg(PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5),
+ arg6, 6);
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7>
+inline sqlite3_stmt* PrepareQuery(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7) {
+ return BindArg(PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6),
+ arg7, 7);
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7, typename Type8>
+inline sqlite3_stmt* PrepareQuery(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7, const Type8& arg8) {
+ return BindArg(PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7),
+ arg8, 8);
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7, typename Type8,
+ typename Type9>
+inline sqlite3_stmt* PrepareQuery(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7, const Type8& arg8,
+ const Type9& arg9) {
+ return BindArg(PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8),
+ arg9, 9);
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7, typename Type8,
+ typename Type9, typename Type10>
+inline sqlite3_stmt* PrepareQuery(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7, const Type8& arg8,
+ const Type9& arg9, const Type10& arg10) {
+ return BindArg(PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8, arg9),
+ arg10, 10);
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7, typename Type8,
+ typename Type9, typename Type10, typename Type11>
+inline sqlite3_stmt* PrepareQuery(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7, const Type8& arg8,
+ const Type9& arg9, const Type10& arg10,
+ const Type11& arg11) {
+ return BindArg(PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8, arg9, arg10),
+ arg11, 11);
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7, typename Type8,
+ typename Type9, typename Type10, typename Type11, typename Type12>
+inline sqlite3_stmt* PrepareQuery(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7, const Type8& arg8,
+ const Type9& arg9, const Type10& arg10,
+ const Type11& arg11, const Type12& arg12) {
+ return BindArg(PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8, arg9, arg10, arg11),
+ arg12, 12);
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7, typename Type8,
+ typename Type9, typename Type10, typename Type11, typename Type12,
+ typename Type13>
+inline sqlite3_stmt* PrepareQuery(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7, const Type8& arg8,
+ const Type9& arg9, const Type10& arg10,
+ const Type11& arg11, const Type12& arg12,
+ const Type13& arg13) {
+ return BindArg(PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8, arg9, arg10, arg11, arg12),
+ arg13, 13);
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7, typename Type8,
+ typename Type9, typename Type10, typename Type11, typename Type12,
+ typename Type13, typename Type14>
+inline sqlite3_stmt* PrepareQuery(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7, const Type8& arg8,
+ const Type9& arg9, const Type10& arg10,
+ const Type11& arg11, const Type12& arg12,
+ const Type13& arg13, const Type14& arg14) {
+ return BindArg(PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13),
+ arg14, 14);
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7, typename Type8,
+ typename Type9, typename Type10, typename Type11, typename Type12,
+ typename Type13, typename Type14, typename Type15>
+inline sqlite3_stmt* PrepareQuery(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7, const Type8& arg8,
+ const Type9& arg9, const Type10& arg10,
+ const Type11& arg11, const Type12& arg12,
+ const Type13& arg13, const Type14& arg14,
+ const Type15& arg15) {
+ return BindArg(PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14),
+ arg15, 15);
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7, typename Type8,
+ typename Type9, typename Type10, typename Type11, typename Type12,
+ typename Type13, typename Type14, typename Type15, typename Type16>
+inline sqlite3_stmt* PrepareQuery(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7, const Type8& arg8,
+ const Type9& arg9, const Type10& arg10,
+ const Type11& arg11, const Type12& arg12,
+ const Type13& arg13, const Type14& arg14,
+ const Type15& arg15, const Type16& arg16) {
+ return BindArg(PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15),
+ arg16, 16);
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7, typename Type8,
+ typename Type9, typename Type10, typename Type11, typename Type12,
+ typename Type13, typename Type14, typename Type15, typename Type16,
+ typename Type17>
+inline sqlite3_stmt* PrepareQuery(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7, const Type8& arg8,
+ const Type9& arg9, const Type10& arg10,
+ const Type11& arg11, const Type12& arg12,
+ const Type13& arg13, const Type14& arg14,
+ const Type15& arg15, const Type16& arg16,
+ const Type17& arg17) {
+ return BindArg(PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16),
+ arg17, 17);
+}
+
+void ExecOrDie(sqlite3* dbhandle, const char* query);
+
+// Finalizes (deletes) the query before returning.
+void ExecOrDie(sqlite3* dbhandle, const char* query, sqlite3_stmt* statement);
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7, typename Type8,
+ typename Type9, typename Type10>
+inline void ExecOrDie(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7, const Type8& arg8,
+ const Type9& arg9, const Type10& arg10) {
+ return ExecOrDie(dbhandle, query,
+ PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8, arg9, arg10));
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7, typename Type8,
+ typename Type9>
+inline void ExecOrDie(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7, const Type8& arg8,
+ const Type9& arg9) {
+ return ExecOrDie(dbhandle, query,
+ PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8, arg9));
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7, typename Type8>
+inline void ExecOrDie(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7, const Type8& arg8) {
+ return ExecOrDie(dbhandle, query,
+ PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8));
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7>
+inline void ExecOrDie(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7) {
+ return ExecOrDie(dbhandle, query,
+ PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7));
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6>
+inline void ExecOrDie(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6) {
+ return ExecOrDie(dbhandle, query, PrepareQuery(dbhandle, query, arg1, arg2,
+ arg3, arg4, arg5, arg6));
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5>
+inline void ExecOrDie(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5) {
+ return ExecOrDie(dbhandle, query, PrepareQuery(dbhandle, query, arg1, arg2,
+ arg3, arg4, arg5));
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4>
+inline void ExecOrDie(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4) {
+ return ExecOrDie(dbhandle, query, PrepareQuery(dbhandle, query, arg1, arg2,
+ arg3, arg4));
+}
+
+template <typename Type1, typename Type2, typename Type3>
+inline void ExecOrDie(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3) {
+ return ExecOrDie(dbhandle, query, PrepareQuery(dbhandle, query, arg1, arg2,
+ arg3));
+}
+
+template <typename Type1, typename Type2>
+inline void ExecOrDie(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2) {
+ return ExecOrDie(dbhandle, query, PrepareQuery(dbhandle, query, arg1, arg2));
+}
+
+template <typename Type1>
+inline void ExecOrDie(sqlite3* dbhandle, const char* query,
+ const Type1& arg1) {
+ return ExecOrDie(dbhandle, query, PrepareQuery(dbhandle, query, arg1));
+}
+
+
+int Exec(sqlite3* dbhandle, const char* query);
+// Finalizes (deletes) the query before returning.
+int Exec(sqlite3* dbhandle, const char* query, sqlite3_stmt* statement);
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7, typename Type8,
+ typename Type9, typename Type10, typename Type11, typename Type12,
+ typename Type13, typename Type14, typename Type15, typename Type16,
+ typename Type17>
+inline int Exec(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7, const Type8& arg8,
+ const Type9& arg9, const Type10& arg10,
+ const Type11& arg11, const Type12& arg12,
+ const Type13& arg13, const Type14& arg14,
+ const Type15& arg15, const Type16& arg16,
+ const Type17& arg17) {
+ return Exec(dbhandle, query,
+ PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13,
+ arg14, arg15, arg16, arg17));
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7, typename Type8,
+ typename Type9, typename Type10, typename Type11, typename Type12,
+ typename Type13, typename Type14, typename Type15, typename Type16>
+inline int Exec(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7, const Type8& arg8,
+ const Type9& arg9, const Type10& arg10,
+ const Type11& arg11, const Type12& arg12,
+ const Type13& arg13, const Type14& arg14,
+ const Type15& arg15, const Type16& arg16) {
+ return Exec(dbhandle, query,
+ PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13,
+ arg14, arg15, arg16));
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7, typename Type8,
+ typename Type9, typename Type10, typename Type11, typename Type12,
+ typename Type13, typename Type14, typename Type15>
+inline int Exec(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7, const Type8& arg8,
+ const Type9& arg9, const Type10& arg10,
+ const Type11& arg11, const Type12& arg12,
+ const Type13& arg13, const Type14& arg14,
+ const Type15& arg15) {
+ return Exec(dbhandle, query,
+ PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13,
+ arg14, arg15));
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7, typename Type8,
+ typename Type9, typename Type10, typename Type11, typename Type12,
+ typename Type13, typename Type14>
+inline int Exec(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7, const Type8& arg8,
+ const Type9& arg9, const Type10& arg10,
+ const Type11& arg11, const Type12& arg12,
+ const Type13& arg13, const Type14& arg14) {
+ return Exec(dbhandle, query,
+ PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13,
+ arg14));
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7, typename Type8,
+ typename Type9, typename Type10, typename Type11, typename Type12,
+ typename Type13>
+inline int Exec(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7, const Type8& arg8,
+ const Type9& arg9, const Type10& arg10,
+ const Type11& arg11, const Type12& arg12,
+ const Type13& arg13) {
+ return Exec(dbhandle, query,
+ PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13));
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7, typename Type8,
+ typename Type9, typename Type10, typename Type11, typename Type12>
+inline int Exec(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7, const Type8& arg8,
+ const Type9& arg9, const Type10& arg10,
+ const Type11& arg11, const Type12& arg12) {
+ return Exec(dbhandle, query,
+ PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8, arg9, arg10, arg11, arg12));
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7, typename Type8,
+ typename Type9, typename Type10, typename Type11>
+inline int Exec(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7, const Type8& arg8,
+ const Type9& arg9, const Type10& arg10,
+ const Type11& arg11) {
+ return Exec(dbhandle, query,
+ PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8, arg9, arg10, arg11));
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7, typename Type8,
+ typename Type9, typename Type10>
+inline int Exec(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7, const Type8& arg8,
+ const Type9& arg9, const Type10& arg10) {
+ return Exec(dbhandle, query,
+ PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8, arg9, arg10));
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7, typename Type8,
+ typename Type9>
+inline int Exec(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7, const Type8& arg8,
+ const Type9& arg9) {
+ return Exec(dbhandle, query,
+ PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8, arg9));
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7, typename Type8>
+inline int Exec(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7, const Type8& arg8) {
+ return Exec(dbhandle, query,
+ PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8));
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6, typename Type7>
+inline int Exec(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6,
+ const Type7& arg7) {
+ return Exec(dbhandle, query,
+ PrepareQuery(dbhandle, query, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7));
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5, typename Type6>
+inline int Exec(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5, const Type6& arg6) {
+ return Exec(dbhandle, query, PrepareQuery(dbhandle, query, arg1, arg2,
+ arg3, arg4, arg5, arg6));
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4,
+ typename Type5>
+inline int Exec(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4,
+ const Type5& arg5) {
+ return Exec(dbhandle, query, PrepareQuery(dbhandle, query, arg1, arg2,
+ arg3, arg4, arg5));
+}
+
+template <typename Type1, typename Type2, typename Type3, typename Type4>
+inline int Exec(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3, const Type4& arg4) {
+ return Exec(dbhandle, query, PrepareQuery(dbhandle, query, arg1, arg2,
+ arg3, arg4));
+}
+
+template <typename Type1, typename Type2, typename Type3>
+inline int Exec(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2,
+ const Type3& arg3) {
+ return Exec(dbhandle, query, PrepareQuery(dbhandle, query, arg1, arg2,
+ arg3));
+}
+
+template <typename Type1, typename Type2>
+inline int Exec(sqlite3* dbhandle, const char* query,
+ const Type1& arg1, const Type2& arg2) {
+ return Exec(dbhandle, query, PrepareQuery(dbhandle, query, arg1, arg2));
+}
+
+template <typename Type1>
+inline int Exec(sqlite3* dbhandle, const char* query,
+ const Type1& arg1) {
+ return Exec(dbhandle, query, PrepareQuery(dbhandle, query, arg1));
+}
+
+
+// Holds an sqlite3_stmt* and automatically finalizes when passes out of scope.
+class ScopedStatement {
+ public:
+ explicit ScopedStatement(sqlite3_stmt* statement = 0)
+ : statement_(statement) { }
+ ~ScopedStatement();
+
+ sqlite3_stmt* get() const { return statement_; }
+
+ // Finalizes currently held statement and sets to new one.
+ void reset(sqlite3_stmt* statement);
+ protected:
+ sqlite3_stmt* statement_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedStatement);
+};
+
+
+// Holds an sqlite3_stmt* and automatically resets when passes out of scope.
+class ScopedStatementResetter {
+ public:
+ explicit ScopedStatementResetter(sqlite3_stmt* statement)
+ : statement_(statement) { }
+ ~ScopedStatementResetter();
+
+ protected:
+ sqlite3_stmt* const statement_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedStatementResetter);
+};
+
+// Useful for encoding any sequence of bytes into a string that can be used in
+// a table name. Kind of like hex encoding, except that A is zero and P is 15.
+std::string APEncode(const std::string& in);
+std::string APDecode(const std::string& in);
+
+#endif // CHROME_BROWSER_SYNC_UTIL_QUERY_HELPERS_H_
diff --git a/chrome/browser/sync/util/query_helpers_unittest.cc b/chrome/browser/sync/util/query_helpers_unittest.cc
new file mode 100644
index 0000000..8be295d
--- /dev/null
+++ b/chrome/browser/sync/util/query_helpers_unittest.cc
@@ -0,0 +1,36 @@
+// Copyright (c) 2009 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 "chrome/browser/sync/util/query_helpers.h"
+
+#include <limits>
+#include <string>
+
+#include "chrome/browser/sync/util/compat-file.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::numeric_limits;
+using std::string;
+
+TEST(QueryHelpers, APEncode) {
+ string test;
+ char i;
+ for (i = numeric_limits<char>::min(); i < numeric_limits<char>::max(); ++i)
+ test.push_back(i);
+ test.push_back(i);
+ const string encoded = APEncode(test);
+ const string decoded = APDecode(encoded);
+ ASSERT_EQ(test, decoded);
+}
+
+TEST(QueryHelpers, TestExecFailure) {
+ sqlite3* database;
+ const PathString test_database(PSTR("queryhelper_test.sqlite3"));
+ PathRemove(test_database);
+ ASSERT_EQ(SQLITE_OK, SqliteOpen(test_database, &database));
+ EXPECT_EQ(SQLITE_DONE, Exec(database, "CREATE TABLE test_table (idx int)"));
+ EXPECT_NE(SQLITE_DONE, Exec(database, "ALTER TABLE test_table ADD COLUMN "
+ "broken int32 default ?", -1));
+ PathRemove(test_database);
+}
diff --git a/chrome/browser/sync/util/row_iterator.h b/chrome/browser/sync/util/row_iterator.h
new file mode 100644
index 0000000..73748ee
--- /dev/null
+++ b/chrome/browser/sync/util/row_iterator.h
@@ -0,0 +1,122 @@
+// Copyright (c) 2009 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 handy type for iterating through query results.
+//
+// Just define your Traits type with
+//
+// RowType
+// Extract(statement*, RowType);
+//
+// and then pass an sqlite3_stmt into the constructor for begin,
+// and use the no-arg constructor for an end iterator. Ex:
+//
+// for (RowIterator<SomeTraits> i(statement), end; i != end; ++i)
+// ...
+
+#ifndef CHROME_BROWSER_SYNC_UTIL_ROW_ITERATOR_H_
+#define CHROME_BROWSER_SYNC_UTIL_ROW_ITERATOR_H_
+
+#include "base/logging.h"
+#include "third_party/sqlite/preprocessed/sqlite3.h"
+
+template <typename ColumnType, int index = 0>
+struct SingleColumnTraits {
+ typedef ColumnType RowType;
+ inline void Extract(sqlite3_stmt* statement, ColumnType* x) const {
+ GetColumn(statement, index, x);
+ }
+};
+
+template <typename RowTraits>
+class RowIterator : public std::iterator<std::input_iterator_tag,
+ const typename RowTraits::RowType> {
+ public:
+ typedef typename RowTraits::RowType RowType;
+ // Statement must have been prepared, but not yet stepped:
+ RowIterator(sqlite3_stmt* statement, RowTraits traits = RowTraits()) {
+ kernel_ = new Kernel;
+ kernel_->done = false;
+ kernel_->refcount = 1;
+ kernel_->statement = statement;
+ kernel_->row_traits = traits;
+ ++(*this);
+ }
+ RowIterator() : kernel_(NULL) { } // creates end iterator
+
+ RowIterator(const RowIterator& i)
+ : kernel_(NULL) {
+ *this = i;
+ }
+
+ ~RowIterator() {
+ if (kernel_ && 0 == --(kernel_->refcount)) {
+ sqlite3_finalize(kernel_->statement);
+ delete kernel_;
+ }
+ }
+
+ RowIterator& operator = (const RowIterator& i) {
+ if (kernel_ && (0 == --(kernel_->refcount))) {
+ sqlite3_finalize(kernel_->statement);
+ delete kernel_;
+ }
+ kernel_ = i.kernel_;
+ if (kernel_)
+ kernel_->refcount += 1;
+ return *this;
+ }
+
+ RowIterator operator ++(int) {
+ RowIterator i(*this);
+ return ++i;
+ }
+
+ RowIterator& operator ++() {
+ DCHECK(NULL != kernel_);
+ if (SQLITE_ROW == sqlite3_step(kernel_->statement)) {
+ kernel_->row_traits.Extract(kernel_->statement, &kernel_->row);
+ } else {
+ kernel_->done = true;
+ }
+ return *this;
+ }
+
+ const RowType& operator *() const {
+ return *(operator -> ());
+ }
+
+ const RowType* operator ->() const {
+ DCHECK(NULL != kernel_);
+ DCHECK(!kernel_->done);
+ return &(kernel_->row);
+ }
+
+ bool operator == (const RowIterator& i) const {
+ if (kernel_ == i.kernel_)
+ return true;
+ if (NULL == kernel_ && i.kernel_->done)
+ return true;
+ if (NULL == i.kernel_ && kernel_->done)
+ return true;
+ return false;
+ }
+
+ bool operator != (const RowIterator& i) const {
+ return !(*this == i);
+ }
+
+ protected:
+ struct Kernel {
+ int refcount;
+ bool done;
+ RowType row;
+ sqlite3_stmt* statement;
+ RowTraits row_traits;
+ };
+
+ Kernel* kernel_;
+};
+
+#endif // CHROME_BROWSER_SYNC_UTIL_ROW_ITERATOR_H_
diff --git a/chrome/browser/sync/util/signin.h b/chrome/browser/sync/util/signin.h
new file mode 100644
index 0000000..0664d38
--- /dev/null
+++ b/chrome/browser/sync/util/signin.h
@@ -0,0 +1,15 @@
+// Copyright (c) 2009 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 CHROME_BROWSER_SYNC_UTIL_SIGNIN_H_
+#define CHROME_BROWSER_SYNC_UTIL_SIGNIN_H_
+
+// This enumeration is here since we used to support hosted and non-hosted
+// accounts, but now only the latter is supported.
+enum SignIn {
+ // The account foo@domain is authenticated as a consumer account.
+ GMAIL_SIGNIN
+};
+
+#endif // CHROME_BROWSER_SYNC_UTIL_SIGNIN_H_
diff --git a/chrome/browser/sync/util/sync_types.h b/chrome/browser/sync/util/sync_types.h
new file mode 100644
index 0000000..7a08575
--- /dev/null
+++ b/chrome/browser/sync/util/sync_types.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2009 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 CHROME_BROWSER_SYNC_UTIL_SYNC_TYPES_H_
+#define CHROME_BROWSER_SYNC_UTIL_SYNC_TYPES_H_
+
+#include <iosfwd>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/string_util.h"
+
+// TODO(timsteele): Use base/file_path.h instead of PathString.
+#ifdef OS_WINDOWS
+#define PATHSTRING_IS_STD_STRING 0
+typedef std::wstring PathString;
+
+// This ugly double define hack is needed to allow the following pattern on
+// Windows:
+//
+// #define FOO "Foo"
+// #define FOO_PATH_STRING PSTR("Foo")
+//
+// TODO(sync): find out if we can avoid this.
+#define PSTR_UGLY_DOUBLE_DEFINE_HACK(s) L##s
+#define PSTR(s) PSTR_UGLY_DOUBLE_DEFINE_HACK(s)
+#define PSTR_CHAR wchar_t
+
+inline size_t PathLen(const wchar_t* s) {
+ return wcslen(s);
+}
+
+#else // Mac and Linux
+#define PATHSTRING_IS_STD_STRING 1
+#define PSTR_CHAR char
+typedef string PathString;
+#define PSTR(s) s
+inline size_t PathLen(const char* s) {
+ return strlen(s);
+}
+// Mac OS X typedef's BOOL to signed char, so we do that on Linux too.
+typedef signed char BOOL;
+typedef int32 LONG;
+typedef uint32 DWORD;
+typedef int64 LONGLONG;
+typedef uint64 ULONGLONG;
+
+#define MAX_PATH PATH_MAX
+#if !defined(TRUE)
+const BOOL TRUE = 1;
+#endif
+#if !defined(FALSE)
+const BOOL FALSE = 0;
+#endif
+#endif
+
+typedef PathString::value_type PathChar;
+
+inline size_t CountBytes(const std::wstring& s) {
+ return s.size() * sizeof(std::wstring::value_type);
+}
+
+inline size_t CountBytes(const std::string &s) {
+ return s.size() * sizeof(std::string::value_type);
+}
+
+inline PathString IntToPathString(int digit) {
+ std::string tmp = StringPrintf("%d", digit);
+ return PathString(tmp.begin(), tmp.end());
+}
+
+const int kSyncProtocolMaxNameLengthBytes = 255;
+
+#endif // CHROME_BROWSER_SYNC_UTIL_SYNC_TYPES_H_
diff --git a/chrome/browser/sync/util/user_settings-posix.cc b/chrome/browser/sync/util/user_settings-posix.cc
new file mode 100644
index 0000000..091e7e3
--- /dev/null
+++ b/chrome/browser/sync/util/user_settings-posix.cc
@@ -0,0 +1,34 @@
+// Copyright (c) 2009 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 entry.
+
+// Implement the storage of service tokens in memory
+
+#include "chrome/browser/sync/util/user_settings.h"
+
+namespace browser_sync {
+
+void UserSettings::ClearAllServiceTokens() {
+ service_tokens_.clear();
+}
+
+void UserSettings::SetAuthTokenForService(const string& email,
+ const string& service_name, const string& long_lived_service_token) {
+ service_tokens_[service_name] = long_lived_service_token;
+}
+
+bool UserSettings::GetLastUserAndServiceToken(const string& service_name,
+ string* username,
+ string* service_token) {
+ ServiceTokenMap::const_iterator iter = service_tokens_.find(service_name);
+
+ if (iter != service_tokens_.end()) {
+ *username = email_;
+ *service_token = iter->second;
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/util/user_settings-win32.cc b/chrome/browser/sync/util/user_settings-win32.cc
new file mode 100644
index 0000000..dac7f21
--- /dev/null
+++ b/chrome/browser/sync/util/user_settings-win32.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2009 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 entry.
+
+#include "chrome/browser/sync/util/user_settings.h"
+
+#include <string>
+
+#include "chrome/browser/sync/util/crypto_helpers.h"
+#include "chrome/browser/sync/util/data_encryption.h"
+#include "chrome/browser/sync/util/query_helpers.h"
+
+using std::string;
+
+namespace browser_sync {
+
+bool UserSettings::GetLastUser(string* username) {
+ ScopedDBHandle dbhandle(this);
+ ScopedStatement query(PrepareQuery(dbhandle.get(),
+ "SELECT email FROM cookies"));
+ if (SQLITE_ROW == sqlite3_step(query.get())) {
+ GetColumn(query.get(), 0, username);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void UserSettings::ClearAllServiceTokens() {
+ ScopedDBHandle dbhandle(this);
+ ExecOrDie(dbhandle.get(), "DELETE FROM cookies");
+}
+
+void UserSettings::SetAuthTokenForService(const string& email,
+ const string& service_name, const string& long_lived_service_token) {
+ ScopedDBHandle dbhandle(this);
+ ExecOrDie(dbhandle.get(), "INSERT INTO cookies "
+ "(email, service_name, service_token) "
+ "values (?, ?, ?)", email, service_name,
+ EncryptData(long_lived_service_token));
+}
+
+// Returns the username whose credentials have been persisted as well as
+// a service token for the named service.
+bool UserSettings::GetLastUserAndServiceToken(const string& service_name,
+ string* username,
+ string* service_token) {
+ ScopedDBHandle dbhandle(this);
+ ScopedStatement query(PrepareQuery(
+ dbhandle.get(),
+ "SELECT email, service_token FROM cookies WHERE service_name = ?",
+ service_name));
+
+ if (SQLITE_ROW == sqlite3_step(query.get())) {
+ GetColumn(query.get(), 0, username);
+
+ std::vector<uint8> encrypted_service_token;
+ GetColumn(query.get(), 1, &encrypted_service_token);
+ DecryptData(encrypted_service_token, service_token);
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace browser_sync
+
diff --git a/chrome/browser/sync/util/user_settings.cc b/chrome/browser/sync/util/user_settings.cc
new file mode 100644
index 0000000..573365a
--- /dev/null
+++ b/chrome/browser/sync/util/user_settings.cc
@@ -0,0 +1,350 @@
+// Copyright (c) 2009 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 entry.
+
+// This class isn't pretty. It's just a step better than globals, which is what
+// these were previously.
+
+#include "chrome/browser/sync/util/user_settings.h"
+
+#if defined(OS_WINDOWS)
+#include <windows.h>
+#endif
+
+#include <string>
+#include <limits>
+#include <vector>
+
+#include "base/file_util.h"
+#include "base/string_util.h"
+#include "chrome/browser/sync/syncable/directory_manager.h" // For migration.
+#include "chrome/browser/sync/util/crypto_helpers.h"
+#include "chrome/browser/sync/util/data_encryption.h"
+#include "chrome/browser/sync/util/path_helpers.h"
+#include "chrome/browser/sync/util/query_helpers.h"
+
+using std::numeric_limits;
+using std::string;
+using std::vector;
+
+using syncable::DirectoryManager;
+
+namespace browser_sync {
+
+static const char PASSWORD_HASH[] = "password_hash2";
+static const char SALT[] = "salt2";
+
+static const int kSaltSize = 20;
+static const int kCurrentDBVersion = 11;
+
+UserSettings::ScopedDBHandle::ScopedDBHandle(UserSettings* settings) :
+ mutex_lock_(&settings->dbhandle_mutex_), handle_(&settings->dbhandle_) {
+}
+
+UserSettings::UserSettings() :
+ dbhandle_(NULL) {
+}
+
+string UserSettings::email() const {
+ ScopedLock lock(&mutex_);
+ return email_;
+}
+
+static void MakeSigninsTable(sqlite3* const dbhandle) {
+ // Multiple email addresses can map to the same Google Account.
+ // This table keeps a map of sign-in email addresses to primary
+ // Google Account email addresses.
+ ExecOrDie(dbhandle, "CREATE TABLE signins"
+ " (signin, primary_email, "
+ " PRIMARY KEY(signin, primary_email) ON CONFLICT REPLACE)");
+}
+
+void UserSettings::MigrateOldVersionsAsNeeded(sqlite3* const handle,
+ int current_version) {
+ switch (current_version) {
+ // Versions 1-9 are unhandled. Version numbers greater than
+ // kCurrentDBVersion should have already been weeded out by the caller.
+ default:
+ // When the version is too old, we just try to continue anyway. There
+ // should not be a released product that makes a database too old for us
+ // to handle.
+ LOG(WARNING) << "UserSettings database version " << current_version <<
+ " is too old to handle.";
+ return;
+ case 10:
+ {
+ // Scrape the 'shares' table to find the syncable DB. 'shares'
+ // had a pair of string columns that mapped the username to the filename
+ // of the sync data sqlite3 file. Version 11 switched to a constant
+ // filename, so here we read the string, copy the file to the new name,
+ // delete the old one, and then drop the unused shares table.
+ ScopedStatement share_query(PrepareQuery(handle,
+ "SELECT share_name, file_name FROM shares"));
+ int query_result = sqlite3_step(share_query.get());
+ CHECK(SQLITE_ROW == query_result);
+ PathString share_name, file_name;
+ GetColumn(share_query.get(), 0, &share_name);
+ GetColumn(share_query.get(), 1, &file_name);
+
+ if (!file_util::Move(file_name,
+ DirectoryManager::GetSyncDataDatabaseFilename())) {
+ LOG(WARNING) << "Unable to upgrade UserSettings from v10";
+ return;
+ }
+ }
+ ExecOrDie(handle, "DROP TABLE shares");
+ ExecOrDie(handle, "UPDATE db_version SET version = 11");
+ // FALL THROUGH
+ case kCurrentDBVersion:
+ // Nothing to migrate.
+ return;
+ }
+}
+
+static void MakeCookiesTable(sqlite3* const dbhandle) {
+ // This table keeps a list of auth tokens for each signed in account. There
+ // will be as many rows as there are auth tokens per sign in.
+ // The service_token column will store encrypted values.
+ ExecOrDie(dbhandle, "CREATE TABLE cookies"
+ " (email, service_name, service_token, "
+ " PRIMARY KEY(email, service_name) ON CONFLICT REPLACE)");
+}
+
+static void MakeSigninTypesTable(sqlite3* const dbhandle) {
+ // With every successful gaia authentication, remember if it was
+ // a hosted domain or not.
+ ExecOrDie(dbhandle, "CREATE TABLE signin_types"
+ " (signin, signin_type, "
+ " PRIMARY KEY(signin, signin_type) ON CONFLICT REPLACE)");
+}
+
+static void MakeClientIDTable(sqlite3* const dbhandle) {
+ // Stores a single client ID value that can be used as the client id,
+ // if there's not another such ID provided on the install.
+ ExecOrDie(dbhandle, "CREATE TABLE client_id (id) ");
+ ExecOrDie(dbhandle, "INSERT INTO client_id values ( ? )",
+ Generate128BitRandomHexString());
+}
+
+bool UserSettings::Init(const PathString& settings_path) {
+ { // Scope the handle
+ ScopedDBHandle dbhandle(this);
+ if (dbhandle_)
+ sqlite3_close(dbhandle_);
+ CHECK(SQLITE_OK == SqliteOpen(settings_path.c_str(), &dbhandle_));
+ // In the worst case scenario, the user may hibernate his computer during
+ // one of our transactions.
+ sqlite3_busy_timeout(dbhandle_, numeric_limits<int>::max());
+
+ int sqlite_result = Exec(dbhandle.get(), "BEGIN EXCLUSIVE TRANSACTION");
+ CHECK(SQLITE_DONE == sqlite_result);
+ ScopedStatement table_query(PrepareQuery(dbhandle.get(),
+ "select count(*) from sqlite_master where type = 'table'"
+ " and name = 'db_version'"));
+ int query_result = sqlite3_step(table_query.get());
+ CHECK(SQLITE_ROW == query_result);
+ int table_count = 0;
+ GetColumn(table_query.get(), 0, &table_count);
+ table_query.reset(NULL);
+ if (table_count > 0) {
+ ScopedStatement version_query(PrepareQuery(dbhandle.get(),
+ "SELECT version FROM db_version"));
+ query_result = sqlite3_step(version_query.get());
+ CHECK(SQLITE_ROW == query_result);
+ const int version = sqlite3_column_int(version_query.get(), 0);
+ version_query.reset(NULL);
+ if (version > kCurrentDBVersion) {
+ LOG(WARNING) << "UserSettings database is too new.";
+ return false;
+ }
+
+ MigrateOldVersionsAsNeeded(dbhandle.get(), version);
+ } else {
+ // Create settings table.
+ ExecOrDie(dbhandle.get(), "CREATE TABLE settings"
+ " (email, key, value, "
+ " PRIMARY KEY(email, key) ON CONFLICT REPLACE)");
+
+ // Create and populate version table.
+ ExecOrDie(dbhandle.get(), "CREATE TABLE db_version ( version )");
+ ExecOrDie(dbhandle.get(), "INSERT INTO db_version values ( ? )",
+ kCurrentDBVersion);
+
+ MakeSigninsTable(dbhandle.get());
+ MakeCookiesTable(dbhandle.get());
+ MakeSigninTypesTable(dbhandle.get());
+ MakeClientIDTable(dbhandle.get());
+ }
+ ExecOrDie(dbhandle.get(), "COMMIT TRANSACTION");
+ }
+#ifdef OS_WINDOWS
+ // Do not index this file. Scanning can occur every time we close the file,
+ // which causes long delays in SQLite's file locking.
+ const DWORD attrs = GetFileAttributes(settings_path.c_str());
+ const BOOL attrs_set =
+ SetFileAttributes(settings_path.c_str(),
+ attrs | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
+#endif
+ return true;
+}
+
+
+UserSettings::~UserSettings() {
+ if (dbhandle_)
+ sqlite3_close(dbhandle_);
+}
+
+const int32 kInvalidHash = 0xFFFFFFFF;
+
+// We use 10 bits of data from the MD5 digest as the hash.
+const int32 kHashMask = 0x3FF;
+
+int32 GetHashFromDigest(const vector<uint8>& digest) {
+ int32 hash = 0;
+ int32 mask = kHashMask;
+ for (vector<uint8>::const_iterator i = digest.begin(); i != digest.end();
+ ++i) {
+ hash = hash << 8;
+ hash = hash | (*i & kHashMask);
+ mask = mask >> 8;
+ if (0 == mask)
+ break;
+ }
+ return hash;
+}
+
+void UserSettings::StoreEmailForSignin(const string& signin,
+ const string& primary_email) {
+ ScopedDBHandle dbhandle(this);
+ ExecOrDie(dbhandle.get(), "BEGIN TRANSACTION");
+ ScopedStatement query(PrepareQuery(dbhandle.get(),
+ "SELECT COUNT(*) FROM signins"
+ " WHERE signin = ? AND primary_email = ?",
+ signin, primary_email));
+ int query_result = sqlite3_step(query.get());
+ CHECK(SQLITE_ROW == query_result);
+ int32 count = 0;
+ GetColumn(query.get(), 0, &count);
+ query.reset(NULL);
+ if (0 == count) {
+ // Migrate any settings the user might have from earlier versions.
+ ExecOrDie(dbhandle.get(), "UPDATE settings SET email = ? WHERE email = ?",
+ primary_email, signin);
+ // Store this signin:email mapping.
+ ExecOrDie(dbhandle.get(), "INSERT INTO signins(signin, primary_email)"
+ " values ( ?, ? )", signin, primary_email);
+ }
+ ExecOrDie(dbhandle.get(), "COMMIT TRANSACTION");
+}
+
+bool UserSettings::GetEmailForSignin(/*in, out*/string* signin) {
+ ScopedDBHandle dbhandle(this);
+ string result;
+ ScopedStatement query(PrepareQuery(dbhandle.get(),
+ "SELECT primary_email FROM signins"
+ " WHERE signin = ?", *signin));
+ int query_result = sqlite3_step(query.get());
+ if (SQLITE_ROW == query_result) {
+ GetColumn(query.get(), 0, &result);
+ if (!result.empty()) {
+ swap(result, *signin);
+ return true;
+ }
+ }
+ return false;
+}
+
+void UserSettings::StoreHashedPassword(const string& email,
+ const string& password) {
+ // Save one-way hashed password:
+ char binary_salt[kSaltSize];
+ {
+ ScopedLock lock(&mutex_);
+ GetRandomBytes(binary_salt, sizeof(binary_salt));
+ }
+ const string salt = APEncode(string(binary_salt, sizeof(binary_salt)));
+ MD5Calculator md5;
+ md5.AddData(salt.data(), salt.size());
+ md5.AddData(password.data(), password.size());
+ ScopedDBHandle dbhandle(this);
+ ExecOrDie(dbhandle.get(), "BEGIN TRANSACTION");
+ ExecOrDie(dbhandle.get(), "INSERT INTO settings(email, key, value )"
+ " values ( ?, ?, ? )", email, PASSWORD_HASH,
+ GetHashFromDigest(md5.GetDigest()));
+ ExecOrDie(dbhandle.get(), "INSERT INTO settings(email, key, value )"
+ " values ( ?, ?, ? )", email, SALT, salt);
+ ExecOrDie(dbhandle.get(), "COMMIT TRANSACTION");
+}
+
+bool UserSettings::VerifyAgainstStoredHash(const string& email,
+ const string& password) {
+ ScopedDBHandle dbhandle(this);
+ string salt_and_digest;
+
+ ScopedStatement query(PrepareQuery(dbhandle.get(),
+ "SELECT key, value FROM settings"
+ " WHERE email = ? AND"
+ " (key = ? OR key = ?)",
+ email, PASSWORD_HASH, SALT));
+ int query_result = sqlite3_step(query.get());
+ string salt;
+ int32 hash = kInvalidHash;
+ while (SQLITE_ROW == query_result) {
+ string key;
+ GetColumn(query.get(), 0, &key);
+ if (key == SALT)
+ GetColumn(query.get(), 1, &salt);
+ else
+ GetColumn(query.get(), 1, &hash);
+ query_result = sqlite3_step(query.get());
+ }
+ CHECK(SQLITE_DONE == query_result);
+ if (salt.empty() || hash == kInvalidHash)
+ return false;
+ MD5Calculator md5;
+ md5.AddData(salt.data(), salt.size());
+ md5.AddData(password.data(), password.size());
+ return hash == GetHashFromDigest(md5.GetDigest());
+}
+
+void UserSettings::SwitchUser(const string& username) {
+ {
+ ScopedLock lock(&mutex_);
+ email_ = username;
+ }
+}
+
+void UserSettings::RememberSigninType(const string& signin, SignIn signin_type)
+{
+ ScopedDBHandle dbhandle(this);
+ ExecOrDie(dbhandle.get(), "INSERT INTO signin_types(signin, signin_type)"
+ " values ( ?, ? )", signin, static_cast<int>(signin_type));
+}
+
+SignIn UserSettings::RecallSigninType(const string& signin, SignIn default_type)
+{
+ ScopedDBHandle dbhandle(this);
+ ScopedStatement query(PrepareQuery(dbhandle.get(),
+ "SELECT signin_type from signin_types"
+ " WHERE signin = ?", signin));
+ int query_result = sqlite3_step(query.get());
+ if (SQLITE_ROW == query_result) {
+ int signin_type;
+ GetColumn(query.get(), 0, &signin_type);
+ return static_cast<SignIn>(signin_type);
+ }
+ return default_type;
+}
+
+string UserSettings::GetClientId() {
+ ScopedDBHandle dbhandle(this);
+ ScopedStatement query(PrepareQuery(dbhandle.get(),
+ "SELECT id FROM client_id"));
+ int query_result = sqlite3_step(query.get());
+ string client_id;
+ if (query_result == SQLITE_ROW)
+ GetColumn(query.get(), 0, &client_id);
+ return client_id;
+}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/util/user_settings.h b/chrome/browser/sync/util/user_settings.h
new file mode 100644
index 0000000..45116a5
--- /dev/null
+++ b/chrome/browser/sync/util/user_settings.h
@@ -0,0 +1,114 @@
+// Copyright (c) 2006-2008 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 CHROME_BROWSER_SYNC_UTIL_USER_SETTINGS_H_
+#define CHROME_BROWSER_SYNC_UTIL_USER_SETTINGS_H_
+
+#include <map>
+#include <set>
+#include <string>
+
+#include "chrome/browser/sync/util/pthread_helpers.h"
+#include "chrome/browser/sync/util/signin.h"
+#include "chrome/browser/sync/util/sync_types.h"
+
+extern "C" struct sqlite3;
+
+namespace browser_sync {
+
+class URLFactory;
+
+class UserSettings {
+ public:
+ // db_path is used for the main user settings.
+ // passwords_file contains hashes of passwords.
+ UserSettings();
+ ~UserSettings();
+ // Returns false (failure) if the db is a newer version.
+ bool Init(const PathString& settings_path);
+ void StoreHashedPassword(const std::string& email,
+ const std::string& password);
+ bool VerifyAgainstStoredHash(const std::string& email,
+ const std::string& password);
+
+ // Set the username.
+ void SwitchUser(const std::string& email);
+
+ // Saves the email address and the named service token for the given user.
+ // Call this multiple times with the same email parameter to save
+ // multiple service tokens.
+ void SetAuthTokenForService(const std::string& email,
+ const std::string& service_name,
+ const std::string& long_lived_service_token);
+ // Erases all saved service tokens.
+ void ClearAllServiceTokens();
+
+ // Returns the user name whose credentials have been persisted.
+ bool GetLastUser(std::string* username);
+
+ // Returns the user name whose credentials have been persisted as well as
+ // a service token for the named service
+ bool GetLastUserAndServiceToken(const std::string& service_name,
+ std::string* username,
+ std::string* service_token);
+
+ void RememberSigninType(const std::string& signin, SignIn signin_type);
+ SignIn RecallSigninType(const std::string& signin, SignIn default_type);
+
+ void RemoveAllGuestSettings();
+
+ void RemoveShare(const PathString& share_path);
+
+ void StoreEmailForSignin(const std::string& signin,
+ const std::string& primary_email);
+
+ // Multiple email addresses can map to the same Google Account. This method
+ // returns the primary Google Account email associated with |signin|, which
+ // is used as both input and output.
+ bool GetEmailForSignin(std::string* signin);
+
+
+ std::string email() const;
+
+ // Get a unique ID suitable for use as the client ID. This ID
+ // has the lifetime of the user settings database. You may use this ID if
+ // your operating environment does not provide its own unique client ID.
+ std::string GetClientId();
+
+ protected:
+ struct ScopedDBHandle {
+ ScopedDBHandle(UserSettings* settings);
+ inline sqlite3* get() const { return *handle_; }
+ PThreadScopedLock<PThreadMutex> mutex_lock_;
+ sqlite3** const handle_;
+ };
+
+ friend struct ScopedDBHandle;
+ friend class URLFactory;
+
+ void MigrateOldVersionsAsNeeded(sqlite3* const handle, int current_version);
+
+ private:
+ std::string email_;
+ mutable PThreadMutex mutex_; // protects email_
+ typedef PThreadScopedLock<PThreadMutex> ScopedLock;
+
+ // We keep a single dbhandle.
+ sqlite3* dbhandle_;
+ PThreadMutex dbhandle_mutex_;
+
+ // TODO(sync): Use in-memory cache for service auth tokens on posix.
+ // Have someone competent in Windows switch it over to not use Sqlite in the
+ // future.
+#ifndef OS_WINDOWS
+ typedef std::map<std::string, std::string> ServiceTokenMap;
+ ServiceTokenMap service_tokens_;
+#endif // OS_WINDOWS
+
+ DISALLOW_COPY_AND_ASSIGN(UserSettings);
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_UTIL_USER_SETTINGS_H_
diff --git a/chrome/browser/sync/util/user_settings_unittest.cc b/chrome/browser/sync/util/user_settings_unittest.cc
new file mode 100644
index 0000000..56c761d
--- /dev/null
+++ b/chrome/browser/sync/util/user_settings_unittest.cc
@@ -0,0 +1,86 @@
+// Copyright (c) 2009 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 entry.
+
+#include "base/file_util.h"
+#include "base/test_file_util.h"
+#include "chrome/browser/sync/syncable/directory_manager.h"
+#include "chrome/browser/sync/util/user_settings.h"
+#include "chrome/browser/sync/util/query_helpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using browser_sync::UserSettings;
+
+static const wchar_t* kV10UserSettingsDB = L"Version10Settings.sqlite3";
+static const wchar_t* kOldStyleSyncDataDB = L"OldStyleSyncData.sqlite3";
+
+class UserSettingsTest : public testing::Test {
+ public:
+ UserSettingsTest() : sync_data_("Some sync data") { }
+ void SetUpVersion10Databases() {
+ CleanUpVersion10Databases();
+ sqlite3* primer_handle = NULL;
+ ASSERT_EQ(SQLITE_OK, SqliteOpen(kV10UserSettingsDB,
+ &primer_handle));
+ FilePath old_sync_data(kOldStyleSyncDataDB);
+
+ ASSERT_EQ(sync_data_.length(), file_util::WriteFile(
+ old_sync_data, sync_data_.data(), sync_data_.length()));
+
+ // Create settings table.
+ ExecOrDie(primer_handle, "CREATE TABLE settings"
+ " (email, key, value, "
+ " PRIMARY KEY(email, key) ON CONFLICT REPLACE)");
+
+ // Create and populate version table.
+ ExecOrDie(primer_handle, "CREATE TABLE db_version ( version )");
+ ExecOrDie(primer_handle, "INSERT INTO db_version values ( ? )", 10);
+ // Create shares table.
+ ExecOrDie(primer_handle, "CREATE TABLE shares"
+ " (email, share_name, file_name,"
+ " PRIMARY KEY(email, share_name) ON CONFLICT REPLACE)");
+ // Populate a share.
+ ExecOrDie(primer_handle, "INSERT INTO shares values ( ?, ?, ?)",
+ "foo@foo.com", "foo@foo.com", WideToUTF8(kOldStyleSyncDataDB));
+ sqlite3_close(primer_handle);
+ }
+
+ void CleanUpVersion10Databases() {
+ ASSERT_TRUE(file_util::DieFileDie(FilePath(kV10UserSettingsDB), false));
+ ASSERT_TRUE(file_util::DieFileDie(FilePath(kOldStyleSyncDataDB), false));
+ ASSERT_TRUE(file_util::DieFileDie(FilePath(L"SyncData.sqlite3"), false));
+ }
+
+ const std::string& sync_data() const { return sync_data_; }
+
+ private:
+ std::string sync_data_;
+};
+
+TEST_F(UserSettingsTest, MigrateFromV10ToV11) {
+ SetUpVersion10Databases();
+ {
+ // Create a UserSettings, which should trigger migration code.
+ // We do this inside a scoped block so it closes itself and we can poke
+ // around to see what happened later.
+ UserSettings settings;
+ settings.Init(kV10UserSettingsDB);
+ }
+
+ // Now poke around using sqlite to see if UserSettings migrated properly.
+ sqlite3* handle = NULL;
+ ASSERT_EQ(SQLITE_OK, SqliteOpen(kV10UserSettingsDB, &handle));
+ ScopedStatement version_query(PrepareQuery(handle,
+ "SELECT version FROM db_version"));
+ ASSERT_EQ(SQLITE_ROW, sqlite3_step(version_query.get()));
+
+ const int version = sqlite3_column_int(version_query.get(), 0);
+ EXPECT_EQ(11, version);
+ EXPECT_FALSE(file_util::PathExists(kOldStyleSyncDataDB));
+
+ std::wstring path(syncable::DirectoryManager::GetSyncDataDatabaseFilename());
+
+ std::string contents;
+ ASSERT_TRUE(file_util::ReadFileToString(path, &contents));
+ EXPECT_EQ(sync_data(), contents);
+}