| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/drive/drive_api_util.h" | |
| 6 | |
| 7 #include <string> | |
| 8 | |
| 9 #include "base/files/file.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "base/md5.h" | |
| 12 #include "base/strings/string16.h" | |
| 13 #include "base/strings/string_util.h" | |
| 14 #include "base/strings/stringprintf.h" | |
| 15 #include "base/strings/utf_string_conversions.h" | |
| 16 #include "base/synchronization/cancellation_flag.h" | |
| 17 #include "base/values.h" | |
| 18 #include "google_apis/drive/drive_api_parser.h" | |
| 19 #include "net/base/escape.h" | |
| 20 #include "net/base/io_buffer.h" | |
| 21 #include "net/base/net_errors.h" | |
| 22 #include "storage/browser/fileapi/file_stream_reader.h" | |
| 23 #include "third_party/re2/re2/re2.h" | |
| 24 #include "url/gurl.h" | |
| 25 | |
| 26 namespace drive { | |
| 27 namespace util { | |
| 28 namespace { | |
| 29 | |
| 30 struct HostedDocumentKind { | |
| 31 const char* mime_type; | |
| 32 const char* extension; | |
| 33 }; | |
| 34 | |
| 35 const HostedDocumentKind kHostedDocumentKinds[] = { | |
| 36 {kGoogleDocumentMimeType, ".gdoc"}, | |
| 37 {kGoogleSpreadsheetMimeType, ".gsheet"}, | |
| 38 {kGooglePresentationMimeType, ".gslides"}, | |
| 39 {kGoogleDrawingMimeType, ".gdraw"}, | |
| 40 {kGoogleTableMimeType, ".gtable"}, | |
| 41 {kGoogleFormMimeType, ".gform"}, | |
| 42 {kGoogleMapMimeType, ".gmaps"}, | |
| 43 }; | |
| 44 | |
| 45 const char kUnknownHostedDocumentExtension[] = ".glink"; | |
| 46 | |
| 47 const int kMd5DigestBufferSize = 512 * 1024; // 512 kB. | |
| 48 | |
| 49 } // namespace | |
| 50 | |
| 51 std::string EscapeQueryStringValue(const std::string& str) { | |
| 52 std::string result; | |
| 53 result.reserve(str.size()); | |
| 54 for (size_t i = 0; i < str.size(); ++i) { | |
| 55 if (str[i] == '\\' || str[i] == '\'') { | |
| 56 result.push_back('\\'); | |
| 57 } | |
| 58 result.push_back(str[i]); | |
| 59 } | |
| 60 return result; | |
| 61 } | |
| 62 | |
| 63 std::string TranslateQuery(const std::string& original_query) { | |
| 64 // In order to handle non-ascii white spaces correctly, convert to UTF16. | |
| 65 base::string16 query = base::UTF8ToUTF16(original_query); | |
| 66 const base::string16 kDelimiter( | |
| 67 base::kWhitespaceUTF16 + base::ASCIIToUTF16("\"")); | |
| 68 | |
| 69 std::string result; | |
| 70 for (size_t index = query.find_first_not_of(base::kWhitespaceUTF16); | |
| 71 index != base::string16::npos; | |
| 72 index = query.find_first_not_of(base::kWhitespaceUTF16, index)) { | |
| 73 bool is_exclusion = (query[index] == '-'); | |
| 74 if (is_exclusion) | |
| 75 ++index; | |
| 76 if (index == query.length()) { | |
| 77 // Here, the token is '-' and it should be ignored. | |
| 78 continue; | |
| 79 } | |
| 80 | |
| 81 size_t begin_token = index; | |
| 82 base::string16 token; | |
| 83 if (query[begin_token] == '"') { | |
| 84 // Quoted query. | |
| 85 ++begin_token; | |
| 86 size_t end_token = query.find('"', begin_token); | |
| 87 if (end_token == base::string16::npos) { | |
| 88 // This is kind of syntax error, since quoted string isn't finished. | |
| 89 // However, the query is built by user manually, so here we treat | |
| 90 // whole remaining string as a token as a fallback, by appending | |
| 91 // a missing double-quote character. | |
| 92 end_token = query.length(); | |
| 93 query.push_back('"'); | |
| 94 } | |
| 95 | |
| 96 token = query.substr(begin_token, end_token - begin_token); | |
| 97 index = end_token + 1; // Consume last '"', too. | |
| 98 } else { | |
| 99 size_t end_token = query.find_first_of(kDelimiter, begin_token); | |
| 100 if (end_token == base::string16::npos) { | |
| 101 end_token = query.length(); | |
| 102 } | |
| 103 | |
| 104 token = query.substr(begin_token, end_token - begin_token); | |
| 105 index = end_token; | |
| 106 } | |
| 107 | |
| 108 if (token.empty()) { | |
| 109 // Just ignore an empty token. | |
| 110 continue; | |
| 111 } | |
| 112 | |
| 113 if (!result.empty()) { | |
| 114 // If there are two or more tokens, need to connect with "and". | |
| 115 result.append(" and "); | |
| 116 } | |
| 117 | |
| 118 // The meaning of "fullText" should include title, description and content. | |
| 119 base::StringAppendF( | |
| 120 &result, | |
| 121 "%sfullText contains \'%s\'", | |
| 122 is_exclusion ? "not " : "", | |
| 123 EscapeQueryStringValue(base::UTF16ToUTF8(token)).c_str()); | |
| 124 } | |
| 125 | |
| 126 return result; | |
| 127 } | |
| 128 | |
| 129 std::string CanonicalizeResourceId(const std::string& resource_id) { | |
| 130 // If resource ID is in the old WAPI format starting with a prefix like | |
| 131 // "document:", strip it and return the remaining part. | |
| 132 std::string stripped_resource_id; | |
| 133 if (RE2::FullMatch(resource_id, "^[a-z-]+(?::|%3A)([\\w-]+)$", | |
| 134 &stripped_resource_id)) | |
| 135 return stripped_resource_id; | |
| 136 return resource_id; | |
| 137 } | |
| 138 | |
| 139 std::string GetMd5Digest(const base::FilePath& file_path, | |
| 140 const base::CancellationFlag* cancellation_flag) { | |
| 141 base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ); | |
| 142 if (!file.IsValid()) | |
| 143 return std::string(); | |
| 144 | |
| 145 base::MD5Context context; | |
| 146 base::MD5Init(&context); | |
| 147 | |
| 148 int64 offset = 0; | |
| 149 scoped_ptr<char[]> buffer(new char[kMd5DigestBufferSize]); | |
| 150 while (true) { | |
| 151 if (cancellation_flag && cancellation_flag->IsSet()) { // Cancelled. | |
| 152 return std::string(); | |
| 153 } | |
| 154 int result = file.Read(offset, buffer.get(), kMd5DigestBufferSize); | |
| 155 if (result < 0) { | |
| 156 // Found an error. | |
| 157 return std::string(); | |
| 158 } | |
| 159 | |
| 160 if (result == 0) { | |
| 161 // End of file. | |
| 162 break; | |
| 163 } | |
| 164 | |
| 165 offset += result; | |
| 166 base::MD5Update(&context, base::StringPiece(buffer.get(), result)); | |
| 167 } | |
| 168 | |
| 169 base::MD5Digest digest; | |
| 170 base::MD5Final(&digest, &context); | |
| 171 return base::MD5DigestToBase16(digest); | |
| 172 } | |
| 173 | |
| 174 FileStreamMd5Digester::FileStreamMd5Digester() | |
| 175 : buffer_(new net::IOBuffer(kMd5DigestBufferSize)) { | |
| 176 } | |
| 177 | |
| 178 FileStreamMd5Digester::~FileStreamMd5Digester() { | |
| 179 } | |
| 180 | |
| 181 void FileStreamMd5Digester::GetMd5Digest( | |
| 182 scoped_ptr<storage::FileStreamReader> stream_reader, | |
| 183 const ResultCallback& callback) { | |
| 184 reader_ = stream_reader.Pass(); | |
| 185 base::MD5Init(&md5_context_); | |
| 186 | |
| 187 // Start the read/hash. | |
| 188 ReadNextChunk(callback); | |
| 189 } | |
| 190 | |
| 191 void FileStreamMd5Digester::ReadNextChunk(const ResultCallback& callback) { | |
| 192 const int result = | |
| 193 reader_->Read(buffer_.get(), kMd5DigestBufferSize, | |
| 194 base::Bind(&FileStreamMd5Digester::OnChunkRead, | |
| 195 base::Unretained(this), callback)); | |
| 196 if (result != net::ERR_IO_PENDING) | |
| 197 OnChunkRead(callback, result); | |
| 198 } | |
| 199 | |
| 200 void FileStreamMd5Digester::OnChunkRead(const ResultCallback& callback, | |
| 201 int bytes_read) { | |
| 202 if (bytes_read < 0) { | |
| 203 // Error - just return empty string. | |
| 204 callback.Run(""); | |
| 205 return; | |
| 206 } else if (bytes_read == 0) { | |
| 207 // EOF. | |
| 208 base::MD5Digest digest; | |
| 209 base::MD5Final(&digest, &md5_context_); | |
| 210 std::string result = base::MD5DigestToBase16(digest); | |
| 211 callback.Run(result); | |
| 212 return; | |
| 213 } | |
| 214 | |
| 215 // Read data and digest it. | |
| 216 base::MD5Update(&md5_context_, | |
| 217 base::StringPiece(buffer_->data(), bytes_read)); | |
| 218 | |
| 219 // Kick off the next read. | |
| 220 ReadNextChunk(callback); | |
| 221 } | |
| 222 | |
| 223 std::string GetHostedDocumentExtension(const std::string& mime_type) { | |
| 224 for (size_t i = 0; i < arraysize(kHostedDocumentKinds); ++i) { | |
| 225 if (mime_type == kHostedDocumentKinds[i].mime_type) | |
| 226 return kHostedDocumentKinds[i].extension; | |
| 227 } | |
| 228 return kUnknownHostedDocumentExtension; | |
| 229 } | |
| 230 | |
| 231 bool IsKnownHostedDocumentMimeType(const std::string& mime_type) { | |
| 232 for (size_t i = 0; i < arraysize(kHostedDocumentKinds); ++i) { | |
| 233 if (mime_type == kHostedDocumentKinds[i].mime_type) | |
| 234 return true; | |
| 235 } | |
| 236 return false; | |
| 237 } | |
| 238 | |
| 239 bool HasHostedDocumentExtension(const base::FilePath& path) { | |
| 240 const std::string extension = base::FilePath(path.Extension()).AsUTF8Unsafe(); | |
| 241 for (size_t i = 0; i < arraysize(kHostedDocumentKinds); ++i) { | |
| 242 if (extension == kHostedDocumentKinds[i].extension) | |
| 243 return true; | |
| 244 } | |
| 245 return extension == kUnknownHostedDocumentExtension; | |
| 246 } | |
| 247 | |
| 248 } // namespace util | |
| 249 } // namespace drive | |
| OLD | NEW |