|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "storage/browser/database/database_util.h" | 
|  |  | 
|  | #include "base/basictypes.h" | 
|  | #include "base/strings/utf_string_conversions.h" | 
|  | #include "storage/browser/database/database_tracker.h" | 
|  | #include "storage/browser/database/vfs_backend.h" | 
|  | #include "storage/common/database/database_identifier.h" | 
|  |  | 
|  | namespace storage { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | bool IsSafeSuffix(const base::string16& suffix) { | 
|  | base::char16 prev_c = 0; | 
|  | for (base::string16::const_iterator it = suffix.begin(); | 
|  | it < suffix.end(); ++it) { | 
|  | base::char16 c = *it; | 
|  | if (!(base::IsAsciiAlpha(c) || base::IsAsciiDigit(c) || | 
|  | c == '-' || c == '.' || c == '_')) { | 
|  | return false; | 
|  | } | 
|  | if (c == '.' && prev_c == '.') | 
|  | return false; | 
|  | prev_c = c; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | const char DatabaseUtil::kJournalFileSuffix[] = "-journal"; | 
|  |  | 
|  | bool DatabaseUtil::CrackVfsFileName(const base::string16& vfs_file_name, | 
|  | std::string* origin_identifier, | 
|  | base::string16* database_name, | 
|  | base::string16* sqlite_suffix) { | 
|  | // 'vfs_file_name' is of the form <origin_identifier>/<db_name>#<suffix>. | 
|  | // <suffix> is optional. | 
|  | DCHECK(!vfs_file_name.empty()); | 
|  | size_t first_slash_index = vfs_file_name.find('/'); | 
|  | size_t last_pound_index = vfs_file_name.rfind('#'); | 
|  | // '/' and '#' must be present in the string. Also, the string cannot start | 
|  | // with a '/' (origin_identifier cannot be empty) and '/' must come before '#' | 
|  | if ((first_slash_index == base::string16::npos) || | 
|  | (last_pound_index == base::string16::npos) || | 
|  | (first_slash_index == 0) || | 
|  | (first_slash_index > last_pound_index)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | std::string origin_id = base::UTF16ToASCII( | 
|  | vfs_file_name.substr(0, first_slash_index)); | 
|  | if (!IsValidOriginIdentifier(origin_id)) | 
|  | return false; | 
|  |  | 
|  | base::string16 suffix = vfs_file_name.substr( | 
|  | last_pound_index + 1, vfs_file_name.length() - last_pound_index - 1); | 
|  | if (!IsSafeSuffix(suffix)) | 
|  | return false; | 
|  |  | 
|  | if (origin_identifier) | 
|  | *origin_identifier = origin_id; | 
|  |  | 
|  | if (database_name) { | 
|  | *database_name = vfs_file_name.substr( | 
|  | first_slash_index + 1, last_pound_index - first_slash_index - 1); | 
|  | } | 
|  |  | 
|  | if (sqlite_suffix) | 
|  | *sqlite_suffix = suffix; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | base::FilePath DatabaseUtil::GetFullFilePathForVfsFile( | 
|  | DatabaseTracker* db_tracker, const base::string16& vfs_file_name) { | 
|  | std::string origin_identifier; | 
|  | base::string16 database_name; | 
|  | base::string16 sqlite_suffix; | 
|  | if (!CrackVfsFileName(vfs_file_name, &origin_identifier, | 
|  | &database_name, &sqlite_suffix)) { | 
|  | return base::FilePath(); // invalid vfs_file_name | 
|  | } | 
|  |  | 
|  | base::FilePath full_path = db_tracker->GetFullDBFilePath( | 
|  | origin_identifier, database_name); | 
|  | if (!full_path.empty() && !sqlite_suffix.empty()) { | 
|  | DCHECK(full_path.Extension().empty()); | 
|  | full_path = full_path.InsertBeforeExtensionASCII( | 
|  | base::UTF16ToASCII(sqlite_suffix)); | 
|  | } | 
|  | // Watch out for directory traversal attempts from a compromised renderer. | 
|  | if (full_path.value().find(FILE_PATH_LITERAL("..")) != | 
|  | base::FilePath::StringType::npos) | 
|  | return base::FilePath(); | 
|  | return full_path; | 
|  | } | 
|  |  | 
|  | bool DatabaseUtil::IsValidOriginIdentifier( | 
|  | const std::string& origin_identifier) { | 
|  | return GetOriginFromIdentifier(origin_identifier).is_valid(); | 
|  | } | 
|  |  | 
|  | }  // namespace storage |