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/google_apis/gdata_wapi_requests.h"
7 #include "base/location.h"
8 #include "base/task_runner_util.h"
9 #include "base/values.h"
10 #include "chrome/browser/google_apis/gdata_wapi_parser.h"
11 #include "chrome/browser/google_apis/gdata_wapi_url_generator.h"
12 #include "chrome/browser/google_apis/request_sender.h"
13 #include "chrome/browser/google_apis/request_util.h"
14 #include "third_party/libxml/chromium/libxml_utils.h"
16 using net::URLFetcher;
18 namespace google_apis {
22 // Parses the JSON value to ResourceList.
23 scoped_ptr<ResourceList> ParseResourceListOnBlockingPool(
24 scoped_ptr<base::Value> value) {
27 return ResourceList::ExtractAndParse(*value);
30 // Runs |callback| with |error| and |resource_list|, but replace the error code
31 // with GDATA_PARSE_ERROR, if there was a parsing error.
32 void DidParseResourceListOnBlockingPool(
33 const GetResourceListCallback& callback,
35 scoped_ptr<ResourceList> resource_list) {
36 DCHECK(!callback.is_null());
38 // resource_list being NULL indicates there was a parsing error.
40 error = GDATA_PARSE_ERROR;
42 callback.Run(error, resource_list.Pass());
45 // Parses the JSON value to ResourceList on the blocking pool and runs
46 // |callback| on the UI thread once parsing is done.
47 void ParseResourceListAndRun(
48 scoped_refptr<base::TaskRunner> blocking_task_runner,
49 const GetResourceListCallback& callback,
51 scoped_ptr<base::Value> value) {
52 DCHECK(!callback.is_null());
55 callback.Run(error, scoped_ptr<ResourceList>());
59 base::PostTaskAndReplyWithResult(
62 base::Bind(&ParseResourceListOnBlockingPool, base::Passed(&value)),
63 base::Bind(&DidParseResourceListOnBlockingPool, callback, error));
66 // Parses the JSON value to AccountMetadata and runs |callback| on the UI
67 // thread once parsing is done.
68 void ParseAccounetMetadataAndRun(const GetAccountMetadataCallback& callback,
70 scoped_ptr<base::Value> value) {
71 DCHECK(!callback.is_null());
74 callback.Run(error, scoped_ptr<AccountMetadata>());
78 // Parsing AccountMetadata is cheap enough to do on UI thread.
79 scoped_ptr<AccountMetadata> entry =
80 google_apis::AccountMetadata::CreateFrom(*value);
82 callback.Run(GDATA_PARSE_ERROR, scoped_ptr<AccountMetadata>());
86 callback.Run(error, entry.Pass());
89 // Parses the |value| to ResourceEntry with error handling.
90 // This is designed to be used for ResumeUploadRequest and
91 // GetUploadStatusRequest.
92 scoped_ptr<ResourceEntry> ParseResourceEntry(scoped_ptr<base::Value> value) {
93 scoped_ptr<ResourceEntry> entry;
95 entry = ResourceEntry::ExtractAndParse(*value);
97 // Note: |value| may be NULL, in particular if the callback is for a
100 LOG(WARNING) << "Invalid entry received on upload.";
106 // Extracts the open link url from the JSON Feed. Used by AuthorizeApp().
107 void ParseOpenLinkAndRun(const std::string& app_id,
108 const AuthorizeAppCallback& callback,
109 GDataErrorCode error,
110 scoped_ptr<base::Value> value) {
111 DCHECK(!callback.is_null());
114 callback.Run(error, GURL());
118 // Parsing ResourceEntry is cheap enough to do on UI thread.
119 scoped_ptr<ResourceEntry> resource_entry = ParseResourceEntry(value.Pass());
120 if (!resource_entry) {
121 callback.Run(GDATA_PARSE_ERROR, GURL());
125 // Look for the link to open the file with the app with |app_id|.
126 const ScopedVector<Link>& resource_links = resource_entry->links();
128 for (size_t i = 0; i < resource_links.size(); ++i) {
129 const Link& link = *resource_links[i];
130 if (link.type() == Link::LINK_OPEN_WITH && link.app_id() == app_id) {
131 open_link = link.href();
136 if (open_link.is_empty())
137 error = GDATA_OTHER_ERROR;
139 callback.Run(error, open_link);
144 //============================ GetResourceListRequest ========================
146 GetResourceListRequest::GetResourceListRequest(
147 RequestSender* sender,
148 const GDataWapiUrlGenerator& url_generator,
149 const GURL& override_url,
150 int64 start_changestamp,
151 const std::string& search_string,
152 const std::string& directory_resource_id,
153 const GetResourceListCallback& callback)
156 base::Bind(&ParseResourceListAndRun,
157 make_scoped_refptr(sender->blocking_task_runner()),
159 url_generator_(url_generator),
160 override_url_(override_url),
161 start_changestamp_(start_changestamp),
162 search_string_(search_string),
163 directory_resource_id_(directory_resource_id) {
164 DCHECK(!callback.is_null());
167 GetResourceListRequest::~GetResourceListRequest() {}
169 GURL GetResourceListRequest::GetURL() const {
170 return url_generator_.GenerateResourceListUrl(override_url_,
173 directory_resource_id_);
176 //============================ SearchByTitleRequest ==========================
178 SearchByTitleRequest::SearchByTitleRequest(
179 RequestSender* sender,
180 const GDataWapiUrlGenerator& url_generator,
181 const std::string& title,
182 const std::string& directory_resource_id,
183 const GetResourceListCallback& callback)
186 base::Bind(&ParseResourceListAndRun,
187 make_scoped_refptr(sender->blocking_task_runner()),
189 url_generator_(url_generator),
191 directory_resource_id_(directory_resource_id) {
192 DCHECK(!callback.is_null());
195 SearchByTitleRequest::~SearchByTitleRequest() {}
197 GURL SearchByTitleRequest::GetURL() const {
198 return url_generator_.GenerateSearchByTitleUrl(
199 title_, directory_resource_id_);
202 //============================ GetResourceEntryRequest =======================
204 GetResourceEntryRequest::GetResourceEntryRequest(
205 RequestSender* sender,
206 const GDataWapiUrlGenerator& url_generator,
207 const std::string& resource_id,
208 const GURL& embed_origin,
209 const GetDataCallback& callback)
210 : GetDataRequest(sender, callback),
211 url_generator_(url_generator),
212 resource_id_(resource_id),
213 embed_origin_(embed_origin) {
214 DCHECK(!callback.is_null());
217 GetResourceEntryRequest::~GetResourceEntryRequest() {}
219 GURL GetResourceEntryRequest::GetURL() const {
220 return url_generator_.GenerateEditUrlWithEmbedOrigin(
221 resource_id_, embed_origin_);
224 //========================= GetAccountMetadataRequest ========================
226 GetAccountMetadataRequest::GetAccountMetadataRequest(
227 RequestSender* sender,
228 const GDataWapiUrlGenerator& url_generator,
229 const GetAccountMetadataCallback& callback,
230 bool include_installed_apps)
231 : GetDataRequest(sender,
232 base::Bind(&ParseAccounetMetadataAndRun, callback)),
233 url_generator_(url_generator),
234 include_installed_apps_(include_installed_apps) {
235 DCHECK(!callback.is_null());
238 GetAccountMetadataRequest::~GetAccountMetadataRequest() {}
240 GURL GetAccountMetadataRequest::GetURL() const {
241 return url_generator_.GenerateAccountMetadataUrl(include_installed_apps_);
244 //=========================== DeleteResourceRequest ==========================
246 DeleteResourceRequest::DeleteResourceRequest(
247 RequestSender* sender,
248 const GDataWapiUrlGenerator& url_generator,
249 const EntryActionCallback& callback,
250 const std::string& resource_id,
251 const std::string& etag)
252 : EntryActionRequest(sender, callback),
253 url_generator_(url_generator),
254 resource_id_(resource_id),
256 DCHECK(!callback.is_null());
259 DeleteResourceRequest::~DeleteResourceRequest() {}
261 GURL DeleteResourceRequest::GetURL() const {
262 return url_generator_.GenerateEditUrl(resource_id_);
265 URLFetcher::RequestType DeleteResourceRequest::GetRequestType() const {
266 return URLFetcher::DELETE_REQUEST;
269 std::vector<std::string>
270 DeleteResourceRequest::GetExtraRequestHeaders() const {
271 std::vector<std::string> headers;
272 headers.push_back(util::GenerateIfMatchHeader(etag_));
276 //========================== CreateDirectoryRequest ==========================
278 CreateDirectoryRequest::CreateDirectoryRequest(
279 RequestSender* sender,
280 const GDataWapiUrlGenerator& url_generator,
281 const GetDataCallback& callback,
282 const std::string& parent_resource_id,
283 const std::string& directory_title)
284 : GetDataRequest(sender, callback),
285 url_generator_(url_generator),
286 parent_resource_id_(parent_resource_id),
287 directory_title_(directory_title) {
288 DCHECK(!callback.is_null());
291 CreateDirectoryRequest::~CreateDirectoryRequest() {}
293 GURL CreateDirectoryRequest::GetURL() const {
294 return url_generator_.GenerateContentUrl(parent_resource_id_);
297 URLFetcher::RequestType
298 CreateDirectoryRequest::GetRequestType() const {
299 return URLFetcher::POST;
302 bool CreateDirectoryRequest::GetContentData(std::string* upload_content_type,
303 std::string* upload_content) {
304 upload_content_type->assign("application/atom+xml");
305 XmlWriter xml_writer;
306 xml_writer.StartWriting();
307 xml_writer.StartElement("entry");
308 xml_writer.AddAttribute("xmlns", "http://www.w3.org/2005/Atom");
310 xml_writer.StartElement("category");
311 xml_writer.AddAttribute("scheme",
312 "http://schemas.google.com/g/2005#kind");
313 xml_writer.AddAttribute("term",
314 "http://schemas.google.com/docs/2007#folder");
315 xml_writer.EndElement(); // Ends "category" element.
317 xml_writer.WriteElement("title", directory_title_);
319 xml_writer.EndElement(); // Ends "entry" element.
320 xml_writer.StopWriting();
321 upload_content->assign(xml_writer.GetWrittenString());
322 DVLOG(1) << "CreateDirectory data: " << *upload_content_type << ", ["
323 << *upload_content << "]";
327 //============================ CopyHostedDocumentRequest =====================
329 CopyHostedDocumentRequest::CopyHostedDocumentRequest(
330 RequestSender* sender,
331 const GDataWapiUrlGenerator& url_generator,
332 const GetDataCallback& callback,
333 const std::string& resource_id,
334 const std::string& new_title)
335 : GetDataRequest(sender, callback),
336 url_generator_(url_generator),
337 resource_id_(resource_id),
338 new_title_(new_title) {
339 DCHECK(!callback.is_null());
342 CopyHostedDocumentRequest::~CopyHostedDocumentRequest() {}
344 URLFetcher::RequestType CopyHostedDocumentRequest::GetRequestType() const {
345 return URLFetcher::POST;
348 GURL CopyHostedDocumentRequest::GetURL() const {
349 return url_generator_.GenerateResourceListRootUrl();
352 bool CopyHostedDocumentRequest::GetContentData(
353 std::string* upload_content_type,
354 std::string* upload_content) {
355 upload_content_type->assign("application/atom+xml");
356 XmlWriter xml_writer;
357 xml_writer.StartWriting();
358 xml_writer.StartElement("entry");
359 xml_writer.AddAttribute("xmlns", "http://www.w3.org/2005/Atom");
361 xml_writer.WriteElement("id", resource_id_);
362 xml_writer.WriteElement("title", new_title_);
364 xml_writer.EndElement(); // Ends "entry" element.
365 xml_writer.StopWriting();
366 upload_content->assign(xml_writer.GetWrittenString());
367 DVLOG(1) << "CopyHostedDocumentRequest data: " << *upload_content_type
368 << ", [" << *upload_content << "]";
372 //=========================== RenameResourceRequest ==========================
374 RenameResourceRequest::RenameResourceRequest(
375 RequestSender* sender,
376 const GDataWapiUrlGenerator& url_generator,
377 const EntryActionCallback& callback,
378 const std::string& resource_id,
379 const std::string& new_title)
380 : EntryActionRequest(sender, callback),
381 url_generator_(url_generator),
382 resource_id_(resource_id),
383 new_title_(new_title) {
384 DCHECK(!callback.is_null());
387 RenameResourceRequest::~RenameResourceRequest() {}
389 URLFetcher::RequestType RenameResourceRequest::GetRequestType() const {
390 return URLFetcher::PUT;
393 std::vector<std::string>
394 RenameResourceRequest::GetExtraRequestHeaders() const {
395 std::vector<std::string> headers;
396 headers.push_back(util::kIfMatchAllHeader);
400 GURL RenameResourceRequest::GetURL() const {
401 return url_generator_.GenerateEditUrl(resource_id_);
404 bool RenameResourceRequest::GetContentData(std::string* upload_content_type,
405 std::string* upload_content) {
406 upload_content_type->assign("application/atom+xml");
407 XmlWriter xml_writer;
408 xml_writer.StartWriting();
409 xml_writer.StartElement("entry");
410 xml_writer.AddAttribute("xmlns", "http://www.w3.org/2005/Atom");
412 xml_writer.WriteElement("title", new_title_);
414 xml_writer.EndElement(); // Ends "entry" element.
415 xml_writer.StopWriting();
416 upload_content->assign(xml_writer.GetWrittenString());
417 DVLOG(1) << "RenameResourceRequest data: " << *upload_content_type << ", ["
418 << *upload_content << "]";
422 //=========================== AuthorizeAppRequest ==========================
424 AuthorizeAppRequest::AuthorizeAppRequest(
425 RequestSender* sender,
426 const GDataWapiUrlGenerator& url_generator,
427 const AuthorizeAppCallback& callback,
428 const std::string& resource_id,
429 const std::string& app_id)
430 : GetDataRequest(sender,
431 base::Bind(&ParseOpenLinkAndRun, app_id, callback)),
432 url_generator_(url_generator),
433 resource_id_(resource_id),
435 DCHECK(!callback.is_null());
438 AuthorizeAppRequest::~AuthorizeAppRequest() {}
440 URLFetcher::RequestType AuthorizeAppRequest::GetRequestType() const {
441 return URLFetcher::PUT;
444 std::vector<std::string>
445 AuthorizeAppRequest::GetExtraRequestHeaders() const {
446 std::vector<std::string> headers;
447 headers.push_back(util::kIfMatchAllHeader);
451 bool AuthorizeAppRequest::GetContentData(std::string* upload_content_type,
452 std::string* upload_content) {
453 upload_content_type->assign("application/atom+xml");
454 XmlWriter xml_writer;
455 xml_writer.StartWriting();
456 xml_writer.StartElement("entry");
457 xml_writer.AddAttribute("xmlns", "http://www.w3.org/2005/Atom");
458 xml_writer.AddAttribute("xmlns:docs", "http://schemas.google.com/docs/2007");
459 xml_writer.WriteElement("docs:authorizedApp", app_id_);
461 xml_writer.EndElement(); // Ends "entry" element.
462 xml_writer.StopWriting();
463 upload_content->assign(xml_writer.GetWrittenString());
464 DVLOG(1) << "AuthorizeAppRequest data: " << *upload_content_type << ", ["
465 << *upload_content << "]";
469 GURL AuthorizeAppRequest::GetURL() const {
470 return url_generator_.GenerateEditUrl(resource_id_);
473 //======================= AddResourceToDirectoryRequest ======================
475 AddResourceToDirectoryRequest::AddResourceToDirectoryRequest(
476 RequestSender* sender,
477 const GDataWapiUrlGenerator& url_generator,
478 const EntryActionCallback& callback,
479 const std::string& parent_resource_id,
480 const std::string& resource_id)
481 : EntryActionRequest(sender, callback),
482 url_generator_(url_generator),
483 parent_resource_id_(parent_resource_id),
484 resource_id_(resource_id) {
485 DCHECK(!callback.is_null());
488 AddResourceToDirectoryRequest::~AddResourceToDirectoryRequest() {}
490 GURL AddResourceToDirectoryRequest::GetURL() const {
491 return url_generator_.GenerateContentUrl(parent_resource_id_);
494 URLFetcher::RequestType
495 AddResourceToDirectoryRequest::GetRequestType() const {
496 return URLFetcher::POST;
499 bool AddResourceToDirectoryRequest::GetContentData(
500 std::string* upload_content_type, std::string* upload_content) {
501 upload_content_type->assign("application/atom+xml");
502 XmlWriter xml_writer;
503 xml_writer.StartWriting();
504 xml_writer.StartElement("entry");
505 xml_writer.AddAttribute("xmlns", "http://www.w3.org/2005/Atom");
507 xml_writer.WriteElement(
508 "id", url_generator_.GenerateEditUrlWithoutParams(resource_id_).spec());
510 xml_writer.EndElement(); // Ends "entry" element.
511 xml_writer.StopWriting();
512 upload_content->assign(xml_writer.GetWrittenString());
513 DVLOG(1) << "AddResourceToDirectoryRequest data: " << *upload_content_type
514 << ", [" << *upload_content << "]";
518 //==================== RemoveResourceFromDirectoryRequest ====================
520 RemoveResourceFromDirectoryRequest::RemoveResourceFromDirectoryRequest(
521 RequestSender* sender,
522 const GDataWapiUrlGenerator& url_generator,
523 const EntryActionCallback& callback,
524 const std::string& parent_resource_id,
525 const std::string& document_resource_id)
526 : EntryActionRequest(sender, callback),
527 url_generator_(url_generator),
528 resource_id_(document_resource_id),
529 parent_resource_id_(parent_resource_id) {
530 DCHECK(!callback.is_null());
533 RemoveResourceFromDirectoryRequest::~RemoveResourceFromDirectoryRequest() {
536 GURL RemoveResourceFromDirectoryRequest::GetURL() const {
537 return url_generator_.GenerateResourceUrlForRemoval(
538 parent_resource_id_, resource_id_);
541 URLFetcher::RequestType
542 RemoveResourceFromDirectoryRequest::GetRequestType() const {
543 return URLFetcher::DELETE_REQUEST;
546 std::vector<std::string>
547 RemoveResourceFromDirectoryRequest::GetExtraRequestHeaders() const {
548 std::vector<std::string> headers;
549 headers.push_back(util::kIfMatchAllHeader);
553 //======================= InitiateUploadNewFileRequest =======================
555 InitiateUploadNewFileRequest::InitiateUploadNewFileRequest(
556 RequestSender* sender,
557 const GDataWapiUrlGenerator& url_generator,
558 const InitiateUploadCallback& callback,
559 const std::string& content_type,
560 int64 content_length,
561 const std::string& parent_resource_id,
562 const std::string& title)
563 : InitiateUploadRequestBase(sender, callback, content_type, content_length),
564 url_generator_(url_generator),
565 parent_resource_id_(parent_resource_id),
569 InitiateUploadNewFileRequest::~InitiateUploadNewFileRequest() {}
571 GURL InitiateUploadNewFileRequest::GetURL() const {
572 return url_generator_.GenerateInitiateUploadNewFileUrl(parent_resource_id_);
575 net::URLFetcher::RequestType
576 InitiateUploadNewFileRequest::GetRequestType() const {
577 return net::URLFetcher::POST;
580 bool InitiateUploadNewFileRequest::GetContentData(
581 std::string* upload_content_type,
582 std::string* upload_content) {
583 upload_content_type->assign("application/atom+xml");
584 XmlWriter xml_writer;
585 xml_writer.StartWriting();
586 xml_writer.StartElement("entry");
587 xml_writer.AddAttribute("xmlns", "http://www.w3.org/2005/Atom");
588 xml_writer.AddAttribute("xmlns:docs",
589 "http://schemas.google.com/docs/2007");
590 xml_writer.WriteElement("title", title_);
591 xml_writer.EndElement(); // Ends "entry" element.
592 xml_writer.StopWriting();
593 upload_content->assign(xml_writer.GetWrittenString());
594 DVLOG(1) << "InitiateUploadNewFile: " << *upload_content_type << ", ["
595 << *upload_content << "]";
599 //===================== InitiateUploadExistingFileRequest ====================
601 InitiateUploadExistingFileRequest::InitiateUploadExistingFileRequest(
602 RequestSender* sender,
603 const GDataWapiUrlGenerator& url_generator,
604 const InitiateUploadCallback& callback,
605 const std::string& content_type,
606 int64 content_length,
607 const std::string& resource_id,
608 const std::string& etag)
609 : InitiateUploadRequestBase(sender, callback, content_type, content_length),
610 url_generator_(url_generator),
611 resource_id_(resource_id),
615 InitiateUploadExistingFileRequest::~InitiateUploadExistingFileRequest() {}
617 GURL InitiateUploadExistingFileRequest::GetURL() const {
618 return url_generator_.GenerateInitiateUploadExistingFileUrl(resource_id_);
621 net::URLFetcher::RequestType
622 InitiateUploadExistingFileRequest::GetRequestType() const {
623 return net::URLFetcher::PUT;
626 bool InitiateUploadExistingFileRequest::GetContentData(
627 std::string* upload_content_type,
628 std::string* upload_content) {
629 // According to the document there is no need to send the content-type.
630 // However, the server would return 500 server error without the
632 // As its workaround, send "text/plain" content-type here.
633 *upload_content_type = "text/plain";
634 *upload_content = "";
638 std::vector<std::string>
639 InitiateUploadExistingFileRequest::GetExtraRequestHeaders() const {
640 std::vector<std::string> headers(
641 InitiateUploadRequestBase::GetExtraRequestHeaders());
642 headers.push_back(util::GenerateIfMatchHeader(etag_));
646 //============================ ResumeUploadRequest ===========================
648 ResumeUploadRequest::ResumeUploadRequest(
649 RequestSender* sender,
650 const UploadRangeCallback& callback,
651 const ProgressCallback& progress_callback,
652 const GURL& upload_location,
653 int64 start_position,
655 int64 content_length,
656 const std::string& content_type,
657 const base::FilePath& local_file_path)
658 : ResumeUploadRequestBase(sender,
666 progress_callback_(progress_callback) {
667 DCHECK(!callback_.is_null());
670 ResumeUploadRequest::~ResumeUploadRequest() {}
672 void ResumeUploadRequest::OnRangeRequestComplete(
673 const UploadRangeResponse& response, scoped_ptr<base::Value> value) {
674 callback_.Run(response, ParseResourceEntry(value.Pass()));
677 void ResumeUploadRequest::OnURLFetchUploadProgress(
678 const URLFetcher* source, int64 current, int64 total) {
679 if (!progress_callback_.is_null())
680 progress_callback_.Run(current, total);
683 //========================== GetUploadStatusRequest ==========================
685 GetUploadStatusRequest::GetUploadStatusRequest(
686 RequestSender* sender,
687 const UploadRangeCallback& callback,
688 const GURL& upload_url,
689 int64 content_length)
690 : GetUploadStatusRequestBase(sender, upload_url, content_length),
691 callback_(callback) {
692 DCHECK(!callback.is_null());
695 GetUploadStatusRequest::~GetUploadStatusRequest() {}
697 void GetUploadStatusRequest::OnRangeRequestComplete(
698 const UploadRangeResponse& response, scoped_ptr<base::Value> value) {
699 callback_.Run(response, ParseResourceEntry(value.Pass()));
702 //========================== DownloadFileRequest ==========================
704 DownloadFileRequest::DownloadFileRequest(
705 RequestSender* sender,
706 const GDataWapiUrlGenerator& url_generator,
707 const DownloadActionCallback& download_action_callback,
708 const GetContentCallback& get_content_callback,
709 const ProgressCallback& progress_callback,
710 const std::string& resource_id,
711 const base::FilePath& output_file_path)
712 : DownloadFileRequestBase(
714 download_action_callback,
715 get_content_callback,
717 url_generator.GenerateDownloadFileUrl(resource_id),
721 DownloadFileRequest::~DownloadFileRequest() {
724 } // namespace google_apis