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 "chrome/browser/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/values.h"
17 #include "google_apis/drive/drive_api_parser.h"
18 #include "net/base/escape.h"
19 #include "net/base/io_buffer.h"
20 #include "net/base/net_errors.h"
21 #include "storage/browser/fileapi/file_stream_reader.h"
22 #include "third_party/re2/re2/re2.h"
29 struct HostedDocumentKind
{
30 const char* mime_type
;
31 const char* extension
;
34 const HostedDocumentKind kHostedDocumentKinds
[] = {
35 {kGoogleDocumentMimeType
, ".gdoc"},
36 {kGoogleSpreadsheetMimeType
, ".gsheet"},
37 {kGooglePresentationMimeType
, ".gslides"},
38 {kGoogleDrawingMimeType
, ".gdraw"},
39 {kGoogleTableMimeType
, ".gtable"},
40 {kGoogleFormMimeType
, ".gform"},
41 {kGoogleMapMimeType
, ".gmaps"},
44 const char kUnknownHostedDocumentExtension
[] = ".glink";
46 const int kMd5DigestBufferSize
= 512 * 1024; // 512 kB.
50 std::string
EscapeQueryStringValue(const std::string
& str
) {
52 result
.reserve(str
.size());
53 for (size_t i
= 0; i
< str
.size(); ++i
) {
54 if (str
[i
] == '\\' || str
[i
] == '\'') {
55 result
.push_back('\\');
57 result
.push_back(str
[i
]);
62 std::string
TranslateQuery(const std::string
& original_query
) {
63 // In order to handle non-ascii white spaces correctly, convert to UTF16.
64 base::string16 query
= base::UTF8ToUTF16(original_query
);
65 const base::string16
kDelimiter(
66 base::kWhitespaceUTF16
+ base::ASCIIToUTF16("\""));
69 for (size_t index
= query
.find_first_not_of(base::kWhitespaceUTF16
);
70 index
!= base::string16::npos
;
71 index
= query
.find_first_not_of(base::kWhitespaceUTF16
, index
)) {
72 bool is_exclusion
= (query
[index
] == '-');
75 if (index
== query
.length()) {
76 // Here, the token is '-' and it should be ignored.
80 size_t begin_token
= index
;
82 if (query
[begin_token
] == '"') {
85 size_t end_token
= query
.find('"', begin_token
);
86 if (end_token
== base::string16::npos
) {
87 // This is kind of syntax error, since quoted string isn't finished.
88 // However, the query is built by user manually, so here we treat
89 // whole remaining string as a token as a fallback, by appending
90 // a missing double-quote character.
91 end_token
= query
.length();
95 token
= query
.substr(begin_token
, end_token
- begin_token
);
96 index
= end_token
+ 1; // Consume last '"', too.
98 size_t end_token
= query
.find_first_of(kDelimiter
, begin_token
);
99 if (end_token
== base::string16::npos
) {
100 end_token
= query
.length();
103 token
= query
.substr(begin_token
, end_token
- begin_token
);
108 // Just ignore an empty token.
112 if (!result
.empty()) {
113 // If there are two or more tokens, need to connect with "and".
114 result
.append(" and ");
117 // The meaning of "fullText" should include title, description and content.
120 "%sfullText contains \'%s\'",
121 is_exclusion
? "not " : "",
122 EscapeQueryStringValue(base::UTF16ToUTF8(token
)).c_str());
128 std::string
CanonicalizeResourceId(const std::string
& resource_id
) {
129 // If resource ID is in the old WAPI format starting with a prefix like
130 // "document:", strip it and return the remaining part.
131 std::string stripped_resource_id
;
132 if (RE2::FullMatch(resource_id
, "^[a-z-]+(?::|%3A)([\\w-]+)$",
133 &stripped_resource_id
))
134 return stripped_resource_id
;
138 std::string
GetMd5Digest(const base::FilePath
& file_path
) {
139 base::File
file(file_path
, base::File::FLAG_OPEN
| base::File::FLAG_READ
);
141 return std::string();
143 base::MD5Context context
;
144 base::MD5Init(&context
);
147 scoped_ptr
<char[]> buffer(new char[kMd5DigestBufferSize
]);
149 int result
= file
.Read(offset
, buffer
.get(), kMd5DigestBufferSize
);
152 return std::string();
161 base::MD5Update(&context
, base::StringPiece(buffer
.get(), result
));
164 base::MD5Digest digest
;
165 base::MD5Final(&digest
, &context
);
166 return MD5DigestToBase16(digest
);
169 FileStreamMd5Digester::FileStreamMd5Digester()
170 : buffer_(new net::IOBuffer(kMd5DigestBufferSize
)) {
173 FileStreamMd5Digester::~FileStreamMd5Digester() {
176 void FileStreamMd5Digester::GetMd5Digest(
177 scoped_ptr
<storage::FileStreamReader
> stream_reader
,
178 const ResultCallback
& callback
) {
179 reader_
= stream_reader
.Pass();
180 base::MD5Init(&md5_context_
);
182 // Start the read/hash.
183 ReadNextChunk(callback
);
186 void FileStreamMd5Digester::ReadNextChunk(const ResultCallback
& callback
) {
188 reader_
->Read(buffer_
.get(), kMd5DigestBufferSize
,
189 base::Bind(&FileStreamMd5Digester::OnChunkRead
,
190 base::Unretained(this), callback
));
191 if (result
!= net::ERR_IO_PENDING
)
192 OnChunkRead(callback
, result
);
195 void FileStreamMd5Digester::OnChunkRead(const ResultCallback
& callback
,
197 if (bytes_read
< 0) {
198 // Error - just return empty string.
201 } else if (bytes_read
== 0) {
203 base::MD5Digest digest
;
204 base::MD5Final(&digest
, &md5_context_
);
205 std::string result
= MD5DigestToBase16(digest
);
206 callback
.Run(result
);
210 // Read data and digest it.
211 base::MD5Update(&md5_context_
,
212 base::StringPiece(buffer_
->data(), bytes_read
));
214 // Kick off the next read.
215 ReadNextChunk(callback
);
218 std::string
GetHostedDocumentExtension(const std::string
& mime_type
) {
219 for (size_t i
= 0; i
< arraysize(kHostedDocumentKinds
); ++i
) {
220 if (mime_type
== kHostedDocumentKinds
[i
].mime_type
)
221 return kHostedDocumentKinds
[i
].extension
;
223 return kUnknownHostedDocumentExtension
;
226 bool IsKnownHostedDocumentMimeType(const std::string
& mime_type
) {
227 for (size_t i
= 0; i
< arraysize(kHostedDocumentKinds
); ++i
) {
228 if (mime_type
== kHostedDocumentKinds
[i
].mime_type
)
234 bool HasHostedDocumentExtension(const base::FilePath
& path
) {
235 const std::string extension
= base::FilePath(path
.Extension()).AsUTF8Unsafe();
236 for (size_t i
= 0; i
< arraysize(kHostedDocumentKinds
); ++i
) {
237 if (extension
== kHostedDocumentKinds
[i
].extension
)
240 return extension
== kUnknownHostedDocumentExtension
;