| // Copyright 2015 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/utility/importer/edge_database_reader_win.h" |
| |
| #include <windows.h> |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <vector> |
| |
| #include "base/memory/ptr_util.h" |
| |
| namespace { |
| |
| // This is an arbitary size chosen for the database error message buffer. |
| const size_t kErrorMessageSize = 1024; |
| // This is the page size of the Edge data. It's unlikely to change. |
| const JET_API_PTR kEdgeDatabasePageSize = 8192; |
| // This is the code page value for a Unicode (UCS-2) column. |
| const unsigned short kJetUnicodeCodePage = 1200; |
| |
| template <typename T> |
| bool ValidateAndConvertValueGeneric(const JET_COLTYP match_column_type, |
| const JET_COLTYP column_type, |
| const std::vector<uint8_t>& column_data, |
| T* value) { |
| if ((column_type == match_column_type) && (column_data.size() == sizeof(T))) { |
| memcpy(value, &column_data[0], sizeof(T)); |
| return true; |
| } |
| return false; |
| } |
| |
| bool ValidateAndConvertValue(const JET_COLTYP column_type, |
| const std::vector<uint8_t>& column_data, |
| bool* value) { |
| if ((column_type == JET_coltypBit) && (column_data.size() == 1)) { |
| *value = (column_data[0] & 1) == 1; |
| return true; |
| } |
| return false; |
| } |
| |
| bool ValidateAndConvertValue(const JET_COLTYP column_type, |
| const std::vector<uint8_t>& column_data, |
| base::string16* value) { |
| if ((column_type == JET_coltypLongText) && |
| ((column_data.size() % sizeof(base::char16)) == 0)) { |
| base::string16& value_ref = *value; |
| size_t char_length = column_data.size() / sizeof(base::char16); |
| value_ref.resize(char_length); |
| memcpy(&value_ref[0], &column_data[0], column_data.size()); |
| // Remove any trailing NUL characters. |
| while (char_length > 0) { |
| if (value_ref[char_length - 1]) |
| break; |
| char_length--; |
| } |
| value_ref.resize(char_length); |
| return true; |
| } |
| return false; |
| } |
| |
| bool ValidateAndConvertValue(const JET_COLTYP column_type, |
| const std::vector<uint8_t>& column_data, |
| GUID* value) { |
| return ValidateAndConvertValueGeneric(JET_coltypGUID, column_type, |
| column_data, value); |
| } |
| |
| bool ValidateAndConvertValue(const JET_COLTYP column_type, |
| const std::vector<uint8_t>& column_data, |
| int32_t* value) { |
| return ValidateAndConvertValueGeneric(JET_coltypLong, column_type, |
| column_data, value); |
| } |
| |
| bool ValidateAndConvertValue(const JET_COLTYP column_type, |
| const std::vector<uint8_t>& column_data, |
| int64_t* value) { |
| return ValidateAndConvertValueGeneric(JET_coltypLongLong, column_type, |
| column_data, value); |
| } |
| |
| bool ValidateAndConvertValue(const JET_COLTYP column_type, |
| const std::vector<uint8_t>& column_data, |
| FILETIME* value) { |
| return ValidateAndConvertValueGeneric(JET_coltypLongLong, column_type, |
| column_data, value); |
| } |
| |
| bool ValidateAndConvertValue(const JET_COLTYP column_type, |
| const std::vector<uint8_t>& column_data, |
| uint32_t* value) { |
| return ValidateAndConvertValueGeneric(JET_coltypUnsignedLong, column_type, |
| column_data, value); |
| } |
| |
| } // namespace |
| |
| base::string16 EdgeErrorObject::GetErrorMessage() const { |
| WCHAR error_message[kErrorMessageSize] = {}; |
| JET_API_PTR err = last_error_; |
| JET_ERR result = JetGetSystemParameter(JET_instanceNil, JET_sesidNil, |
| JET_paramErrorToString, &err, |
| error_message, sizeof(error_message)); |
| if (result != JET_errSuccess) |
| return L""; |
| |
| return error_message; |
| } |
| |
| bool EdgeErrorObject::SetLastError(JET_ERR error) { |
| last_error_ = error; |
| return error == JET_errSuccess; |
| } |
| |
| EdgeDatabaseTableEnumerator::EdgeDatabaseTableEnumerator( |
| const base::string16& table_name, |
| JET_SESID session_id, |
| JET_TABLEID table_id) |
| : table_id_(table_id), table_name_(table_name), session_id_(session_id) {} |
| |
| EdgeDatabaseTableEnumerator::~EdgeDatabaseTableEnumerator() { |
| if (table_id_ != JET_tableidNil) |
| JetCloseTable(session_id_, table_id_); |
| } |
| |
| bool EdgeDatabaseTableEnumerator::Reset() { |
| return SetLastError(JetMove(session_id_, table_id_, JET_MoveFirst, 0)); |
| } |
| |
| bool EdgeDatabaseTableEnumerator::Next() { |
| return SetLastError(JetMove(session_id_, table_id_, JET_MoveNext, 0)); |
| } |
| |
| template <typename T> |
| bool EdgeDatabaseTableEnumerator::RetrieveColumn( |
| const base::string16& column_name, |
| T* value) { |
| const JET_COLUMNBASE& column_base = GetColumnByName(column_name); |
| if (column_base.cbMax == 0) { |
| SetLastError(JET_errColumnNotFound); |
| return false; |
| } |
| if (column_base.coltyp == JET_coltypLongText && |
| column_base.cp != kJetUnicodeCodePage) { |
| SetLastError(JET_errInvalidColumnType); |
| return false; |
| } |
| std::vector<uint8_t> column_data(column_base.cbMax); |
| unsigned long actual_size = 0; |
| JET_ERR err = JetRetrieveColumn(session_id_, table_id_, column_base.columnid, |
| &column_data[0], column_data.size(), |
| &actual_size, 0, nullptr); |
| SetLastError(err); |
| if (err != JET_errSuccess && err != JET_wrnColumnNull) { |
| return false; |
| } |
| |
| if (err == JET_errSuccess) { |
| column_data.resize(actual_size); |
| if (!ValidateAndConvertValue(column_base.coltyp, column_data, value)) { |
| SetLastError(JET_errInvalidColumnType); |
| return false; |
| } |
| } else { |
| *value = T(); |
| } |
| |
| return true; |
| } |
| |
| // Explicitly instantiate implementations of RetrieveColumn for various types. |
| template bool EdgeDatabaseTableEnumerator::RetrieveColumn(const base::string16&, |
| bool*); |
| template bool EdgeDatabaseTableEnumerator::RetrieveColumn(const base::string16&, |
| FILETIME*); |
| template bool EdgeDatabaseTableEnumerator::RetrieveColumn(const base::string16&, |
| GUID*); |
| template bool EdgeDatabaseTableEnumerator::RetrieveColumn(const base::string16&, |
| int32_t*); |
| template bool EdgeDatabaseTableEnumerator::RetrieveColumn(const base::string16&, |
| int64_t*); |
| template bool EdgeDatabaseTableEnumerator::RetrieveColumn(const base::string16&, |
| base::string16*); |
| template bool EdgeDatabaseTableEnumerator::RetrieveColumn(const base::string16&, |
| uint32_t*); |
| |
| const JET_COLUMNBASE& EdgeDatabaseTableEnumerator::GetColumnByName( |
| const base::string16& column_name) { |
| auto found_col = columns_by_name_.find(column_name); |
| if (found_col == columns_by_name_.end()) { |
| JET_COLUMNBASE column_base = {}; |
| column_base.cbStruct = sizeof(JET_COLUMNBASE); |
| if (!SetLastError(JetGetTableColumnInfo( |
| session_id_, table_id_, column_name.c_str(), &column_base, |
| sizeof(column_base), JET_ColInfoBase))) { |
| // 0 indicates an invalid column. |
| column_base.cbMax = 0; |
| } |
| columns_by_name_[column_name] = column_base; |
| found_col = columns_by_name_.find(column_name); |
| } |
| return found_col->second; |
| } |
| |
| EdgeDatabaseReader::~EdgeDatabaseReader() { |
| // We don't need to collect other ID handles, terminating instance |
| // is enough to shut the entire session down. |
| if (instance_id_ != JET_instanceNil) |
| JetTerm(instance_id_); |
| } |
| |
| bool EdgeDatabaseReader::OpenDatabase(const base::string16& database_file) { |
| if (IsOpen()) { |
| SetLastError(JET_errOneDatabasePerSession); |
| return false; |
| } |
| if (!SetLastError(JetSetSystemParameter(nullptr, JET_sesidNil, |
| JET_paramDatabasePageSize, |
| kEdgeDatabasePageSize, nullptr))) |
| return false; |
| if (!SetLastError(JetCreateInstance(&instance_id_, L"EdgeDataImporter"))) |
| return false; |
| if (!SetLastError(JetSetSystemParameter(&instance_id_, JET_sesidNil, |
| JET_paramRecovery, 0, L"Off"))) |
| return false; |
| if (!SetLastError(JetInit(&instance_id_))) |
| return false; |
| if (!SetLastError( |
| JetBeginSession(instance_id_, &session_id_, nullptr, nullptr))) |
| return false; |
| if (!SetLastError(JetAttachDatabase2(session_id_, database_file.c_str(), 0, |
| JET_bitDbReadOnly))) |
| return false; |
| if (!SetLastError(JetOpenDatabase(session_id_, database_file.c_str(), nullptr, |
| &db_id_, JET_bitDbReadOnly))) |
| return false; |
| return true; |
| } |
| |
| std::unique_ptr<EdgeDatabaseTableEnumerator> |
| EdgeDatabaseReader::OpenTableEnumerator(const base::string16& table_name) { |
| JET_TABLEID table_id; |
| |
| if (!IsOpen()) { |
| SetLastError(JET_errDatabaseNotFound); |
| return nullptr; |
| } |
| |
| if (!SetLastError(JetOpenTable(session_id_, db_id_, table_name.c_str(), |
| nullptr, 0, JET_bitTableReadOnly, &table_id))) |
| return nullptr; |
| |
| return base::WrapUnique( |
| new EdgeDatabaseTableEnumerator(table_name, session_id_, table_id)); |
| } |