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 |