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.
5 #include "components/drive/drive_api_util.h"
9 #include "base/files/file.h"
10 #include "base/logging.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"
30 struct HostedDocumentKind
{
31 const char* mime_type
;
32 const char* extension
;
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"},
45 const char kUnknownHostedDocumentExtension
[] = ".glink";
47 const int kMd5DigestBufferSize
= 512 * 1024; // 512 kB.
51 std::string
EscapeQueryStringValue(const std::string
& str
) {
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('\\');
58 result
.push_back(str
[i
]);
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("\""));
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
] == '-');
76 if (index
== query
.length()) {
77 // Here, the token is '-' and it should be ignored.
81 size_t begin_token
= index
;
83 if (query
[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();
96 token
= query
.substr(begin_token
, end_token
- begin_token
);
97 index
= end_token
+ 1; // Consume last '"', too.
99 size_t end_token
= query
.find_first_of(kDelimiter
, begin_token
);
100 if (end_token
== base::string16::npos
) {
101 end_token
= query
.length();
104 token
= query
.substr(begin_token
, end_token
- begin_token
);
109 // Just ignore an empty token.
113 if (!result
.empty()) {
114 // If there are two or more tokens, need to connect with "and".
115 result
.append(" and ");
118 // The meaning of "fullText" should include title, description and content.
121 "%sfullText contains \'%s\'",
122 is_exclusion
? "not " : "",
123 EscapeQueryStringValue(base::UTF16ToUTF8(token
)).c_str());
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
;
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
);
143 return std::string();
145 base::MD5Context context
;
146 base::MD5Init(&context
);
149 scoped_ptr
<char[]> buffer(new char[kMd5DigestBufferSize
]);
151 if (cancellation_flag
&& cancellation_flag
->IsSet()) { // Cancelled.
152 return std::string();
154 int result
= file
.Read(offset
, buffer
.get(), kMd5DigestBufferSize
);
157 return std::string();
166 base::MD5Update(&context
, base::StringPiece(buffer
.get(), result
));
169 base::MD5Digest digest
;
170 base::MD5Final(&digest
, &context
);
171 return base::MD5DigestToBase16(digest
);
174 FileStreamMd5Digester::FileStreamMd5Digester()
175 : buffer_(new net::IOBuffer(kMd5DigestBufferSize
)) {
178 FileStreamMd5Digester::~FileStreamMd5Digester() {
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_
);
187 // Start the read/hash.
188 ReadNextChunk(callback
);
191 void FileStreamMd5Digester::ReadNextChunk(const ResultCallback
& callback
) {
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
);
200 void FileStreamMd5Digester::OnChunkRead(const ResultCallback
& callback
,
202 if (bytes_read
< 0) {
203 // Error - just return empty string.
206 } else if (bytes_read
== 0) {
208 base::MD5Digest digest
;
209 base::MD5Final(&digest
, &md5_context_
);
210 std::string result
= base::MD5DigestToBase16(digest
);
211 callback
.Run(result
);
215 // Read data and digest it.
216 base::MD5Update(&md5_context_
,
217 base::StringPiece(buffer_
->data(), bytes_read
));
219 // Kick off the next read.
220 ReadNextChunk(callback
);
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
;
228 return kUnknownHostedDocumentExtension
;
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
)
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
)
245 return extension
== kUnknownHostedDocumentExtension
;