Roll src/third_party/WebKit 8b42d1d:744641d (svn 186770:186771)
[chromium-blink-merge.git] / chrome / browser / drive / drive_api_util.cc
blob36397f310ec672e889785d1992c6e0c7ed727c15
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"
7 #include <string>
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/values.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "google_apis/drive/drive_api_parser.h"
19 #include "google_apis/drive/gdata_wapi_parser.h"
20 #include "net/base/escape.h"
21 #include "third_party/re2/re2/re2.h"
22 #include "url/gurl.h"
24 namespace drive {
25 namespace util {
26 namespace {
28 struct HostedDocumentKind {
29 const char* mime_type;
30 const char* extension;
33 const HostedDocumentKind kHostedDocumentKinds[] = {
34 {kGoogleDocumentMimeType, ".gdoc"},
35 {kGoogleSpreadsheetMimeType, ".gsheet"},
36 {kGooglePresentationMimeType, ".gslides"},
37 {kGoogleDrawingMimeType, ".gdraw"},
38 {kGoogleTableMimeType, ".gtable"},
39 {kGoogleFormMimeType, ".gform"},
40 {kGoogleMapMimeType, ".gmaps"},
43 const char kUnknownHostedDocumentExtension[] = ".glink";
45 } // namespace
47 std::string EscapeQueryStringValue(const std::string& str) {
48 std::string result;
49 result.reserve(str.size());
50 for (size_t i = 0; i < str.size(); ++i) {
51 if (str[i] == '\\' || str[i] == '\'') {
52 result.push_back('\\');
54 result.push_back(str[i]);
56 return result;
59 std::string TranslateQuery(const std::string& original_query) {
60 // In order to handle non-ascii white spaces correctly, convert to UTF16.
61 base::string16 query = base::UTF8ToUTF16(original_query);
62 const base::string16 kDelimiter(
63 base::kWhitespaceUTF16 + base::ASCIIToUTF16("\""));
65 std::string result;
66 for (size_t index = query.find_first_not_of(base::kWhitespaceUTF16);
67 index != base::string16::npos;
68 index = query.find_first_not_of(base::kWhitespaceUTF16, index)) {
69 bool is_exclusion = (query[index] == '-');
70 if (is_exclusion)
71 ++index;
72 if (index == query.length()) {
73 // Here, the token is '-' and it should be ignored.
74 continue;
77 size_t begin_token = index;
78 base::string16 token;
79 if (query[begin_token] == '"') {
80 // Quoted query.
81 ++begin_token;
82 size_t end_token = query.find('"', begin_token);
83 if (end_token == base::string16::npos) {
84 // This is kind of syntax error, since quoted string isn't finished.
85 // However, the query is built by user manually, so here we treat
86 // whole remaining string as a token as a fallback, by appending
87 // a missing double-quote character.
88 end_token = query.length();
89 query.push_back('"');
92 token = query.substr(begin_token, end_token - begin_token);
93 index = end_token + 1; // Consume last '"', too.
94 } else {
95 size_t end_token = query.find_first_of(kDelimiter, begin_token);
96 if (end_token == base::string16::npos) {
97 end_token = query.length();
100 token = query.substr(begin_token, end_token - begin_token);
101 index = end_token;
104 if (token.empty()) {
105 // Just ignore an empty token.
106 continue;
109 if (!result.empty()) {
110 // If there are two or more tokens, need to connect with "and".
111 result.append(" and ");
114 // The meaning of "fullText" should include title, description and content.
115 base::StringAppendF(
116 &result,
117 "%sfullText contains \'%s\'",
118 is_exclusion ? "not " : "",
119 EscapeQueryStringValue(base::UTF16ToUTF8(token)).c_str());
122 return result;
125 std::string ExtractResourceIdFromUrl(const GURL& url) {
126 return net::UnescapeURLComponent(url.ExtractFileName(),
127 net::UnescapeRule::URL_SPECIAL_CHARS);
130 std::string CanonicalizeResourceId(const std::string& resource_id) {
131 // If resource ID is in the old WAPI format starting with a prefix like
132 // "document:", strip it and return the remaining part.
133 std::string stripped_resource_id;
134 if (RE2::FullMatch(resource_id, "^[a-z-]+(?::|%3A)([\\w-]+)$",
135 &stripped_resource_id))
136 return stripped_resource_id;
137 return resource_id;
140 scoped_ptr<google_apis::ResourceEntry>
141 ConvertFileResourceToResourceEntry(
142 const google_apis::FileResource& file_resource) {
143 scoped_ptr<google_apis::ResourceEntry> entry(new google_apis::ResourceEntry);
145 // ResourceEntry
146 entry->set_resource_id(file_resource.file_id());
147 entry->set_id(file_resource.file_id());
148 if (file_resource.IsDirectory())
149 entry->set_kind(google_apis::ResourceEntry::ENTRY_KIND_FOLDER);
150 else if (file_resource.IsHostedDocument())
151 entry->set_kind(google_apis::ResourceEntry::ENTRY_KIND_UNKNOWN);
152 else
153 entry->set_kind(google_apis::ResourceEntry::ENTRY_KIND_FILE);
154 entry->set_title(file_resource.title());
155 entry->set_published_time(file_resource.created_date());
157 std::vector<std::string> labels;
158 if (!file_resource.shared_with_me_date().is_null())
159 labels.push_back("shared-with-me");
160 if (file_resource.shared())
161 labels.push_back("shared");
162 entry->set_labels(labels);
164 // This should be the url to download the file_resource.
166 google_apis::Content content;
167 content.set_mime_type(file_resource.mime_type());
168 entry->set_content(content);
170 // TODO(kochi): entry->resource_links_
172 // For file entries
173 entry->set_filename(file_resource.title());
174 entry->set_suggested_filename(file_resource.title());
175 entry->set_file_md5(file_resource.md5_checksum());
176 entry->set_file_size(file_resource.file_size());
178 // If file is removed completely, that information is only available in
179 // ChangeResource, and is reflected in |removed_|. If file is trashed, the
180 // file entry still exists but with its "trashed" label true.
181 entry->set_deleted(file_resource.labels().is_trashed());
183 // ImageMediaMetadata
184 entry->set_image_width(file_resource.image_media_metadata().width());
185 entry->set_image_height(file_resource.image_media_metadata().height());
186 entry->set_image_rotation(file_resource.image_media_metadata().rotation());
188 // CommonMetadata
189 entry->set_etag(file_resource.etag());
190 // entry->authors_
191 // entry->links_.
192 ScopedVector<google_apis::Link> links;
193 for (size_t i = 0; i < file_resource.parents().size(); ++i) {
194 google_apis::Link* link = new google_apis::Link;
195 link->set_type(google_apis::Link::LINK_PARENT);
196 link->set_href(file_resource.parents()[i].parent_link());
197 links.push_back(link);
199 if (!file_resource.alternate_link().is_empty()) {
200 google_apis::Link* link = new google_apis::Link;
201 link->set_type(google_apis::Link::LINK_ALTERNATE);
202 link->set_href(file_resource.alternate_link());
203 links.push_back(link);
205 entry->set_links(links.Pass());
207 // entry->categories_
208 entry->set_updated_time(file_resource.modified_date());
209 entry->set_last_viewed_time(file_resource.last_viewed_by_me_date());
211 entry->FillRemainingFields();
212 return entry.Pass();
215 scoped_ptr<google_apis::ResourceEntry>
216 ConvertChangeResourceToResourceEntry(
217 const google_apis::ChangeResource& change_resource) {
218 scoped_ptr<google_apis::ResourceEntry> entry;
219 if (change_resource.file())
220 entry = ConvertFileResourceToResourceEntry(*change_resource.file()).Pass();
221 else
222 entry.reset(new google_apis::ResourceEntry);
224 entry->set_resource_id(change_resource.file_id());
225 // If |is_deleted()| returns true, the file is removed from Drive.
226 entry->set_removed(change_resource.is_deleted());
227 entry->set_changestamp(change_resource.change_id());
228 entry->set_modification_date(change_resource.modification_date());
230 return entry.Pass();
233 scoped_ptr<google_apis::ResourceList>
234 ConvertFileListToResourceList(const google_apis::FileList& file_list) {
235 scoped_ptr<google_apis::ResourceList> feed(new google_apis::ResourceList);
237 const ScopedVector<google_apis::FileResource>& items = file_list.items();
238 ScopedVector<google_apis::ResourceEntry> entries;
239 for (size_t i = 0; i < items.size(); ++i)
240 entries.push_back(ConvertFileResourceToResourceEntry(*items[i]).release());
241 feed->set_entries(entries.Pass());
243 ScopedVector<google_apis::Link> links;
244 if (!file_list.next_link().is_empty()) {
245 google_apis::Link* link = new google_apis::Link;
246 link->set_type(google_apis::Link::LINK_NEXT);
247 link->set_href(file_list.next_link());
248 links.push_back(link);
250 feed->set_links(links.Pass());
252 return feed.Pass();
255 scoped_ptr<google_apis::ResourceList>
256 ConvertChangeListToResourceList(const google_apis::ChangeList& change_list) {
257 scoped_ptr<google_apis::ResourceList> feed(new google_apis::ResourceList);
259 const ScopedVector<google_apis::ChangeResource>& items = change_list.items();
260 ScopedVector<google_apis::ResourceEntry> entries;
261 for (size_t i = 0; i < items.size(); ++i) {
262 entries.push_back(
263 ConvertChangeResourceToResourceEntry(*items[i]).release());
265 feed->set_entries(entries.Pass());
267 feed->set_largest_changestamp(change_list.largest_change_id());
269 ScopedVector<google_apis::Link> links;
270 if (!change_list.next_link().is_empty()) {
271 google_apis::Link* link = new google_apis::Link;
272 link->set_type(google_apis::Link::LINK_NEXT);
273 link->set_href(change_list.next_link());
274 links.push_back(link);
276 feed->set_links(links.Pass());
278 return feed.Pass();
281 std::string GetMd5Digest(const base::FilePath& file_path) {
282 const int kBufferSize = 512 * 1024; // 512kB.
284 base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
285 if (!file.IsValid())
286 return std::string();
288 base::MD5Context context;
289 base::MD5Init(&context);
291 int64 offset = 0;
292 scoped_ptr<char[]> buffer(new char[kBufferSize]);
293 while (true) {
294 int result = file.Read(offset, buffer.get(), kBufferSize);
295 if (result < 0) {
296 // Found an error.
297 return std::string();
300 if (result == 0) {
301 // End of file.
302 break;
305 offset += result;
306 base::MD5Update(&context, base::StringPiece(buffer.get(), result));
309 base::MD5Digest digest;
310 base::MD5Final(&digest, &context);
311 return MD5DigestToBase16(digest);
314 std::string GetHostedDocumentExtension(const std::string& mime_type) {
315 for (size_t i = 0; i < arraysize(kHostedDocumentKinds); ++i) {
316 if (mime_type == kHostedDocumentKinds[i].mime_type)
317 return kHostedDocumentKinds[i].extension;
319 return kUnknownHostedDocumentExtension;
322 bool IsKnownHostedDocumentMimeType(const std::string& mime_type) {
323 for (size_t i = 0; i < arraysize(kHostedDocumentKinds); ++i) {
324 if (mime_type == kHostedDocumentKinds[i].mime_type)
325 return true;
327 return false;
330 bool HasHostedDocumentExtension(const base::FilePath& path) {
331 const std::string extension = base::FilePath(path.Extension()).AsUTF8Unsafe();
332 for (size_t i = 0; i < arraysize(kHostedDocumentKinds); ++i) {
333 if (extension == kHostedDocumentKinds[i].extension)
334 return true;
336 return extension == kUnknownHostedDocumentExtension;
339 } // namespace util
340 } // namespace drive