Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / chrome / browser / drive / drive_api_util.cc
blobb7b350a028616857cb39f7e71c46c6eb708d5a97
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 std::string GetMimeTypeFromEntryKind(google_apis::DriveEntryKind kind) {
29 switch (kind) {
30 case google_apis::ENTRY_KIND_DOCUMENT:
31 return kGoogleDocumentMimeType;
32 case google_apis::ENTRY_KIND_SPREADSHEET:
33 return kGoogleSpreadsheetMimeType;
34 case google_apis::ENTRY_KIND_PRESENTATION:
35 return kGooglePresentationMimeType;
36 case google_apis::ENTRY_KIND_DRAWING:
37 return kGoogleDrawingMimeType;
38 case google_apis::ENTRY_KIND_TABLE:
39 return kGoogleTableMimeType;
40 case google_apis::ENTRY_KIND_FORM:
41 return kGoogleFormMimeType;
42 default:
43 return std::string();
47 // Returns the argument string.
48 std::string Identity(const std::string& resource_id) { return resource_id; }
50 } // namespace
53 std::string EscapeQueryStringValue(const std::string& str) {
54 std::string result;
55 result.reserve(str.size());
56 for (size_t i = 0; i < str.size(); ++i) {
57 if (str[i] == '\\' || str[i] == '\'') {
58 result.push_back('\\');
60 result.push_back(str[i]);
62 return result;
65 std::string TranslateQuery(const std::string& original_query) {
66 // In order to handle non-ascii white spaces correctly, convert to UTF16.
67 base::string16 query = base::UTF8ToUTF16(original_query);
68 const base::string16 kDelimiter(
69 base::kWhitespaceUTF16 +
70 base::string16(1, static_cast<base::char16>('"')));
72 std::string result;
73 for (size_t index = query.find_first_not_of(base::kWhitespaceUTF16);
74 index != base::string16::npos;
75 index = query.find_first_not_of(base::kWhitespaceUTF16, index)) {
76 bool is_exclusion = (query[index] == '-');
77 if (is_exclusion)
78 ++index;
79 if (index == query.length()) {
80 // Here, the token is '-' and it should be ignored.
81 continue;
84 size_t begin_token = index;
85 base::string16 token;
86 if (query[begin_token] == '"') {
87 // Quoted query.
88 ++begin_token;
89 size_t end_token = query.find('"', begin_token);
90 if (end_token == base::string16::npos) {
91 // This is kind of syntax error, since quoted string isn't finished.
92 // However, the query is built by user manually, so here we treat
93 // whole remaining string as a token as a fallback, by appending
94 // a missing double-quote character.
95 end_token = query.length();
96 query.push_back('"');
99 token = query.substr(begin_token, end_token - begin_token);
100 index = end_token + 1; // Consume last '"', too.
101 } else {
102 size_t end_token = query.find_first_of(kDelimiter, begin_token);
103 if (end_token == base::string16::npos) {
104 end_token = query.length();
107 token = query.substr(begin_token, end_token - begin_token);
108 index = end_token;
111 if (token.empty()) {
112 // Just ignore an empty token.
113 continue;
116 if (!result.empty()) {
117 // If there are two or more tokens, need to connect with "and".
118 result.append(" and ");
121 // The meaning of "fullText" should include title, description and content.
122 base::StringAppendF(
123 &result,
124 "%sfullText contains \'%s\'",
125 is_exclusion ? "not " : "",
126 EscapeQueryStringValue(base::UTF16ToUTF8(token)).c_str());
129 return result;
132 std::string ExtractResourceIdFromUrl(const GURL& url) {
133 return net::UnescapeURLComponent(url.ExtractFileName(),
134 net::UnescapeRule::URL_SPECIAL_CHARS);
137 std::string CanonicalizeResourceId(const std::string& resource_id) {
138 // If resource ID is in the old WAPI format starting with a prefix like
139 // "document:", strip it and return the remaining part.
140 std::string stripped_resource_id;
141 if (RE2::FullMatch(resource_id, "^[a-z-]+(?::|%3A)([\\w-]+)$",
142 &stripped_resource_id))
143 return stripped_resource_id;
144 return resource_id;
147 ResourceIdCanonicalizer GetIdentityResourceIdCanonicalizer() {
148 return base::Bind(&Identity);
151 const char kDocsListScope[] = "https://docs.google.com/feeds/";
152 const char kDriveAppsScope[] = "https://www.googleapis.com/auth/drive.apps";
154 void ParseShareUrlAndRun(const google_apis::GetShareUrlCallback& callback,
155 google_apis::GDataErrorCode error,
156 scoped_ptr<base::Value> value) {
157 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
159 if (!value) {
160 callback.Run(error, GURL());
161 return;
164 // Parsing ResourceEntry is cheap enough to do on UI thread.
165 scoped_ptr<google_apis::ResourceEntry> entry =
166 google_apis::ResourceEntry::ExtractAndParse(*value);
167 if (!entry) {
168 callback.Run(google_apis::GDATA_PARSE_ERROR, GURL());
169 return;
172 const google_apis::Link* share_link =
173 entry->GetLinkByType(google_apis::Link::LINK_SHARE);
174 callback.Run(error, share_link ? share_link->href() : GURL());
177 scoped_ptr<google_apis::FileResource> ConvertResourceEntryToFileResource(
178 const google_apis::ResourceEntry& entry) {
179 scoped_ptr<google_apis::FileResource> file(new google_apis::FileResource);
181 file->set_file_id(entry.resource_id());
182 file->set_title(entry.title());
183 file->set_created_date(entry.published_time());
185 if (std::find(entry.labels().begin(), entry.labels().end(),
186 "shared-with-me") != entry.labels().end()) {
187 // Set current time to mark the file is shared_with_me, since ResourceEntry
188 // doesn't have |shared_with_me_date| equivalent.
189 file->set_shared_with_me_date(base::Time::Now());
192 file->set_shared(std::find(entry.labels().begin(), entry.labels().end(),
193 "shared") != entry.labels().end());
195 if (entry.is_folder()) {
196 file->set_mime_type(kDriveFolderMimeType);
197 } else {
198 std::string mime_type = GetMimeTypeFromEntryKind(entry.kind());
199 if (mime_type.empty())
200 mime_type = entry.content_mime_type();
201 file->set_mime_type(mime_type);
204 file->set_md5_checksum(entry.file_md5());
205 file->set_file_size(entry.file_size());
207 file->mutable_labels()->set_trashed(entry.deleted());
208 file->set_etag(entry.etag());
210 google_apis::ImageMediaMetadata* image_media_metadata =
211 file->mutable_image_media_metadata();
212 image_media_metadata->set_width(entry.image_width());
213 image_media_metadata->set_height(entry.image_height());
214 image_media_metadata->set_rotation(entry.image_rotation());
216 std::vector<google_apis::ParentReference>* parents = file->mutable_parents();
217 for (size_t i = 0; i < entry.links().size(); ++i) {
218 using google_apis::Link;
219 const Link& link = *entry.links()[i];
220 switch (link.type()) {
221 case Link::LINK_PARENT: {
222 google_apis::ParentReference parent;
223 parent.set_parent_link(link.href());
225 std::string file_id =
226 drive::util::ExtractResourceIdFromUrl(link.href());
227 parent.set_file_id(file_id);
228 parents->push_back(parent);
229 break;
231 case Link::LINK_ALTERNATE:
232 file->set_alternate_link(link.href());
233 break;
234 default:
235 break;
239 file->set_modified_date(entry.updated_time());
240 file->set_last_viewed_by_me_date(entry.last_viewed_time());
242 return file.Pass();
245 google_apis::DriveEntryKind GetKind(
246 const google_apis::FileResource& file_resource) {
247 if (file_resource.IsDirectory())
248 return google_apis::ENTRY_KIND_FOLDER;
250 const std::string& mime_type = file_resource.mime_type();
251 if (mime_type == kGoogleDocumentMimeType)
252 return google_apis::ENTRY_KIND_DOCUMENT;
253 if (mime_type == kGoogleSpreadsheetMimeType)
254 return google_apis::ENTRY_KIND_SPREADSHEET;
255 if (mime_type == kGooglePresentationMimeType)
256 return google_apis::ENTRY_KIND_PRESENTATION;
257 if (mime_type == kGoogleDrawingMimeType)
258 return google_apis::ENTRY_KIND_DRAWING;
259 if (mime_type == kGoogleTableMimeType)
260 return google_apis::ENTRY_KIND_TABLE;
261 if (mime_type == kGoogleFormMimeType)
262 return google_apis::ENTRY_KIND_FORM;
263 if (mime_type == "application/pdf")
264 return google_apis::ENTRY_KIND_PDF;
265 return google_apis::ENTRY_KIND_FILE;
268 scoped_ptr<google_apis::ResourceEntry>
269 ConvertFileResourceToResourceEntry(
270 const google_apis::FileResource& file_resource) {
271 scoped_ptr<google_apis::ResourceEntry> entry(new google_apis::ResourceEntry);
273 // ResourceEntry
274 entry->set_resource_id(file_resource.file_id());
275 entry->set_id(file_resource.file_id());
276 entry->set_kind(GetKind(file_resource));
277 entry->set_title(file_resource.title());
278 entry->set_published_time(file_resource.created_date());
280 std::vector<std::string> labels;
281 if (!file_resource.shared_with_me_date().is_null())
282 labels.push_back("shared-with-me");
283 if (file_resource.shared())
284 labels.push_back("shared");
285 entry->set_labels(labels);
287 // This should be the url to download the file_resource.
289 google_apis::Content content;
290 content.set_mime_type(file_resource.mime_type());
291 entry->set_content(content);
293 // TODO(kochi): entry->resource_links_
295 // For file entries
296 entry->set_filename(file_resource.title());
297 entry->set_suggested_filename(file_resource.title());
298 entry->set_file_md5(file_resource.md5_checksum());
299 entry->set_file_size(file_resource.file_size());
301 // If file is removed completely, that information is only available in
302 // ChangeResource, and is reflected in |removed_|. If file is trashed, the
303 // file entry still exists but with its "trashed" label true.
304 entry->set_deleted(file_resource.labels().is_trashed());
306 // ImageMediaMetadata
307 entry->set_image_width(file_resource.image_media_metadata().width());
308 entry->set_image_height(file_resource.image_media_metadata().height());
309 entry->set_image_rotation(file_resource.image_media_metadata().rotation());
311 // CommonMetadata
312 entry->set_etag(file_resource.etag());
313 // entry->authors_
314 // entry->links_.
315 ScopedVector<google_apis::Link> links;
316 for (size_t i = 0; i < file_resource.parents().size(); ++i) {
317 google_apis::Link* link = new google_apis::Link;
318 link->set_type(google_apis::Link::LINK_PARENT);
319 link->set_href(file_resource.parents()[i].parent_link());
320 links.push_back(link);
322 if (!file_resource.alternate_link().is_empty()) {
323 google_apis::Link* link = new google_apis::Link;
324 link->set_type(google_apis::Link::LINK_ALTERNATE);
325 link->set_href(file_resource.alternate_link());
326 links.push_back(link);
328 entry->set_links(links.Pass());
330 // entry->categories_
331 entry->set_updated_time(file_resource.modified_date());
332 entry->set_last_viewed_time(file_resource.last_viewed_by_me_date());
334 entry->FillRemainingFields();
335 return entry.Pass();
338 scoped_ptr<google_apis::ResourceEntry>
339 ConvertChangeResourceToResourceEntry(
340 const google_apis::ChangeResource& change_resource) {
341 scoped_ptr<google_apis::ResourceEntry> entry;
342 if (change_resource.file())
343 entry = ConvertFileResourceToResourceEntry(*change_resource.file()).Pass();
344 else
345 entry.reset(new google_apis::ResourceEntry);
347 entry->set_resource_id(change_resource.file_id());
348 // If |is_deleted()| returns true, the file is removed from Drive.
349 entry->set_removed(change_resource.is_deleted());
350 entry->set_changestamp(change_resource.change_id());
351 entry->set_modification_date(change_resource.modification_date());
353 return entry.Pass();
356 scoped_ptr<google_apis::ResourceList>
357 ConvertFileListToResourceList(const google_apis::FileList& file_list) {
358 scoped_ptr<google_apis::ResourceList> feed(new google_apis::ResourceList);
360 const ScopedVector<google_apis::FileResource>& items = file_list.items();
361 ScopedVector<google_apis::ResourceEntry> entries;
362 for (size_t i = 0; i < items.size(); ++i)
363 entries.push_back(ConvertFileResourceToResourceEntry(*items[i]).release());
364 feed->set_entries(entries.Pass());
366 ScopedVector<google_apis::Link> links;
367 if (!file_list.next_link().is_empty()) {
368 google_apis::Link* link = new google_apis::Link;
369 link->set_type(google_apis::Link::LINK_NEXT);
370 link->set_href(file_list.next_link());
371 links.push_back(link);
373 feed->set_links(links.Pass());
375 return feed.Pass();
378 scoped_ptr<google_apis::ResourceList>
379 ConvertChangeListToResourceList(const google_apis::ChangeList& change_list) {
380 scoped_ptr<google_apis::ResourceList> feed(new google_apis::ResourceList);
382 const ScopedVector<google_apis::ChangeResource>& items = change_list.items();
383 ScopedVector<google_apis::ResourceEntry> entries;
384 for (size_t i = 0; i < items.size(); ++i) {
385 entries.push_back(
386 ConvertChangeResourceToResourceEntry(*items[i]).release());
388 feed->set_entries(entries.Pass());
390 feed->set_largest_changestamp(change_list.largest_change_id());
392 ScopedVector<google_apis::Link> links;
393 if (!change_list.next_link().is_empty()) {
394 google_apis::Link* link = new google_apis::Link;
395 link->set_type(google_apis::Link::LINK_NEXT);
396 link->set_href(change_list.next_link());
397 links.push_back(link);
399 feed->set_links(links.Pass());
401 return feed.Pass();
404 std::string GetMd5Digest(const base::FilePath& file_path) {
405 const int kBufferSize = 512 * 1024; // 512kB.
407 base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
408 if (!file.IsValid())
409 return std::string();
411 base::MD5Context context;
412 base::MD5Init(&context);
414 int64 offset = 0;
415 scoped_ptr<char[]> buffer(new char[kBufferSize]);
416 while (true) {
417 int result = file.Read(offset, buffer.get(), kBufferSize);
418 if (result < 0) {
419 // Found an error.
420 return std::string();
423 if (result == 0) {
424 // End of file.
425 break;
428 offset += result;
429 base::MD5Update(&context, base::StringPiece(buffer.get(), result));
432 base::MD5Digest digest;
433 base::MD5Final(&digest, &context);
434 return MD5DigestToBase16(digest);
437 const char kWapiRootDirectoryResourceId[] = "folder:root";
439 } // namespace util
440 } // namespace drive