gdata_wapi_requests.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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. 4 5#include "google_apis/drive/gdata_wapi_requests.h" 6 7#include "base/location.h" 8#include "base/sequenced_task_runner.h" 9#include "base/task_runner_util.h" 10#include "base/values.h" 11#include "google_apis/drive/gdata_wapi_parser.h" 12#include "google_apis/drive/gdata_wapi_url_generator.h" 13#include "google_apis/drive/request_sender.h" 14#include "google_apis/drive/request_util.h" 15#include "third_party/libxml/chromium/libxml_utils.h" 16 17using net::URLFetcher; 18 19namespace google_apis { 20 21namespace { 22 23// Parses the JSON value to ResourceList. 24scoped_ptr<ResourceList> ParseResourceListOnBlockingPool( 25 scoped_ptr<base::Value> value) { 26 DCHECK(value); 27 28 return ResourceList::ExtractAndParse(*value); 29} 30 31// Runs |callback| with |error| and |resource_list|, but replace the error code 32// with GDATA_PARSE_ERROR, if there was a parsing error. 33void DidParseResourceListOnBlockingPool( 34 const GetResourceListCallback& callback, 35 GDataErrorCode error, 36 scoped_ptr<ResourceList> resource_list) { 37 DCHECK(!callback.is_null()); 38 39 // resource_list being NULL indicates there was a parsing error. 40 if (!resource_list) 41 error = GDATA_PARSE_ERROR; 42 43 callback.Run(error, resource_list.Pass()); 44} 45 46// Parses the JSON value to ResourceList on the blocking pool and runs 47// |callback| on the UI thread once parsing is done. 48void ParseResourceListAndRun( 49 scoped_refptr<base::TaskRunner> blocking_task_runner, 50 const GetResourceListCallback& callback, 51 GDataErrorCode error, 52 scoped_ptr<base::Value> value) { 53 DCHECK(!callback.is_null()); 54 55 if (!value) { 56 callback.Run(error, scoped_ptr<ResourceList>()); 57 return; 58 } 59 60 base::PostTaskAndReplyWithResult( 61 blocking_task_runner, 62 FROM_HERE, 63 base::Bind(&ParseResourceListOnBlockingPool, base::Passed(&value)), 64 base::Bind(&DidParseResourceListOnBlockingPool, callback, error)); 65} 66 67// Parses the JSON value to AccountMetadata and runs |callback| on the UI 68// thread once parsing is done. 69void ParseAccounetMetadataAndRun(const GetAccountMetadataCallback& callback, 70 GDataErrorCode error, 71 scoped_ptr<base::Value> value) { 72 DCHECK(!callback.is_null()); 73 74 if (!value) { 75 callback.Run(error, scoped_ptr<AccountMetadata>()); 76 return; 77 } 78 79 // Parsing AccountMetadata is cheap enough to do on UI thread. 80 scoped_ptr<AccountMetadata> entry = 81 google_apis::AccountMetadata::CreateFrom(*value); 82 if (!entry) { 83 callback.Run(GDATA_PARSE_ERROR, scoped_ptr<AccountMetadata>()); 84 return; 85 } 86 87 callback.Run(error, entry.Pass()); 88} 89 90// Parses the |value| to ResourceEntry with error handling. 91// This is designed to be used for ResumeUploadRequest and 92// GetUploadStatusRequest. 93scoped_ptr<ResourceEntry> ParseResourceEntry(scoped_ptr<base::Value> value) { 94 scoped_ptr<ResourceEntry> entry; 95 if (value.get()) { 96 entry = ResourceEntry::ExtractAndParse(*value); 97 98 // Note: |value| may be NULL, in particular if the callback is for a 99 // failure. 100 if (!entry.get()) 101 LOG(WARNING) << "Invalid entry received on upload."; 102 } 103 104 return entry.Pass(); 105} 106 107// Extracts the open link url from the JSON Feed. Used by AuthorizeApp(). 108void ParseOpenLinkAndRun(const std::string& app_id, 109 const AuthorizeAppCallback& callback, 110 GDataErrorCode error, 111 scoped_ptr<base::Value> value) { 112 DCHECK(!callback.is_null()); 113 114 if (!value) { 115 callback.Run(error, GURL()); 116 return; 117 } 118 119 // Parsing ResourceEntry is cheap enough to do on UI thread. 120 scoped_ptr<ResourceEntry> resource_entry = ParseResourceEntry(value.Pass()); 121 if (!resource_entry) { 122 callback.Run(GDATA_PARSE_ERROR, GURL()); 123 return; 124 } 125 126 // Look for the link to open the file with the app with |app_id|. 127 const ScopedVector<Link>& resource_links = resource_entry->links(); 128 GURL open_link; 129 for (size_t i = 0; i < resource_links.size(); ++i) { 130 const Link& link = *resource_links[i]; 131 if (link.type() == Link::LINK_OPEN_WITH && link.app_id() == app_id) { 132 open_link = link.href(); 133 break; 134 } 135 } 136 137 if (open_link.is_empty()) 138 error = GDATA_OTHER_ERROR; 139 140 callback.Run(error, open_link); 141} 142 143} // namespace 144 145//============================ GetResourceListRequest ======================== 146 147GetResourceListRequest::GetResourceListRequest( 148 RequestSender* sender, 149 const GDataWapiUrlGenerator& url_generator, 150 const GURL& override_url, 151 int64 start_changestamp, 152 const std::string& search_string, 153 const std::string& directory_resource_id, 154 const GetResourceListCallback& callback) 155 : GetDataRequest( 156 sender, 157 base::Bind(&ParseResourceListAndRun, 158 make_scoped_refptr(sender->blocking_task_runner()), 159 callback)), 160 url_generator_(url_generator), 161 override_url_(override_url), 162 start_changestamp_(start_changestamp), 163 search_string_(search_string), 164 directory_resource_id_(directory_resource_id) { 165 DCHECK(!callback.is_null()); 166} 167 168GetResourceListRequest::~GetResourceListRequest() {} 169 170GURL GetResourceListRequest::GetURL() const { 171 return url_generator_.GenerateResourceListUrl(override_url_, 172 start_changestamp_, 173 search_string_, 174 directory_resource_id_); 175} 176 177//============================ SearchByTitleRequest ========================== 178 179SearchByTitleRequest::SearchByTitleRequest( 180 RequestSender* sender, 181 const GDataWapiUrlGenerator& url_generator, 182 const std::string& title, 183 const std::string& directory_resource_id, 184 const GetResourceListCallback& callback) 185 : GetDataRequest( 186 sender, 187 base::Bind(&ParseResourceListAndRun, 188 make_scoped_refptr(sender->blocking_task_runner()), 189 callback)), 190 url_generator_(url_generator), 191 title_(title), 192 directory_resource_id_(directory_resource_id) { 193 DCHECK(!callback.is_null()); 194} 195 196SearchByTitleRequest::~SearchByTitleRequest() {} 197 198GURL SearchByTitleRequest::GetURL() const { 199 return url_generator_.GenerateSearchByTitleUrl( 200 title_, directory_resource_id_); 201} 202 203//============================ GetResourceEntryRequest ======================= 204 205GetResourceEntryRequest::GetResourceEntryRequest( 206 RequestSender* sender, 207 const GDataWapiUrlGenerator& url_generator, 208 const std::string& resource_id, 209 const GURL& embed_origin, 210 const GetDataCallback& callback) 211 : GetDataRequest(sender, callback), 212 url_generator_(url_generator), 213 resource_id_(resource_id), 214 embed_origin_(embed_origin) { 215 DCHECK(!callback.is_null()); 216} 217 218GetResourceEntryRequest::~GetResourceEntryRequest() {} 219 220GURL GetResourceEntryRequest::GetURL() const { 221 return url_generator_.GenerateEditUrlWithEmbedOrigin( 222 resource_id_, embed_origin_); 223} 224 225//========================= GetAccountMetadataRequest ======================== 226 227GetAccountMetadataRequest::GetAccountMetadataRequest( 228 RequestSender* sender, 229 const GDataWapiUrlGenerator& url_generator, 230 const GetAccountMetadataCallback& callback, 231 bool include_installed_apps) 232 : GetDataRequest(sender, 233 base::Bind(&ParseAccounetMetadataAndRun, callback)), 234 url_generator_(url_generator), 235 include_installed_apps_(include_installed_apps) { 236 DCHECK(!callback.is_null()); 237} 238 239GetAccountMetadataRequest::~GetAccountMetadataRequest() {} 240 241GURL GetAccountMetadataRequest::GetURL() const { 242 return url_generator_.GenerateAccountMetadataUrl(include_installed_apps_); 243} 244 245//=========================== DeleteResourceRequest ========================== 246 247DeleteResourceRequest::DeleteResourceRequest( 248 RequestSender* sender, 249 const GDataWapiUrlGenerator& url_generator, 250 const EntryActionCallback& callback, 251 const std::string& resource_id, 252 const std::string& etag) 253 : EntryActionRequest(sender, callback), 254 url_generator_(url_generator), 255 resource_id_(resource_id), 256 etag_(etag) { 257 DCHECK(!callback.is_null()); 258} 259 260DeleteResourceRequest::~DeleteResourceRequest() {} 261 262GURL DeleteResourceRequest::GetURL() const { 263 return url_generator_.GenerateEditUrl(resource_id_); 264} 265 266URLFetcher::RequestType DeleteResourceRequest::GetRequestType() const { 267 return URLFetcher::DELETE_REQUEST; 268} 269 270std::vector<std::string> 271DeleteResourceRequest::GetExtraRequestHeaders() const { 272 std::vector<std::string> headers; 273 headers.push_back(util::GenerateIfMatchHeader(etag_)); 274 return headers; 275} 276 277//========================== CreateDirectoryRequest ========================== 278 279CreateDirectoryRequest::CreateDirectoryRequest( 280 RequestSender* sender, 281 const GDataWapiUrlGenerator& url_generator, 282 const GetDataCallback& callback, 283 const std::string& parent_resource_id, 284 const std::string& directory_title) 285 : GetDataRequest(sender, callback), 286 url_generator_(url_generator), 287 parent_resource_id_(parent_resource_id), 288 directory_title_(directory_title) { 289 DCHECK(!callback.is_null()); 290} 291 292CreateDirectoryRequest::~CreateDirectoryRequest() {} 293 294GURL CreateDirectoryRequest::GetURL() const { 295 return url_generator_.GenerateContentUrl(parent_resource_id_); 296} 297 298URLFetcher::RequestType 299CreateDirectoryRequest::GetRequestType() const { 300 return URLFetcher::POST; 301} 302 303bool CreateDirectoryRequest::GetContentData(std::string* upload_content_type, 304 std::string* upload_content) { 305 upload_content_type->assign("application/atom+xml"); 306 XmlWriter xml_writer; 307 xml_writer.StartWriting(); 308 xml_writer.StartElement("entry"); 309 xml_writer.AddAttribute("xmlns", "http://www.w3.org/2005/Atom"); 310 311 xml_writer.StartElement("category"); 312 xml_writer.AddAttribute("scheme", 313 "http://schemas.google.com/g/2005#kind"); 314 xml_writer.AddAttribute("term", 315 "http://schemas.google.com/docs/2007#folder"); 316 xml_writer.EndElement(); // Ends "category" element. 317 318 xml_writer.WriteElement("title", directory_title_); 319 320 xml_writer.EndElement(); // Ends "entry" element. 321 xml_writer.StopWriting(); 322 upload_content->assign(xml_writer.GetWrittenString()); 323 DVLOG(1) << "CreateDirectory data: " << *upload_content_type << ", [" 324 << *upload_content << "]"; 325 return true; 326} 327 328//=========================== RenameResourceRequest ========================== 329 330RenameResourceRequest::RenameResourceRequest( 331 RequestSender* sender, 332 const GDataWapiUrlGenerator& url_generator, 333 const EntryActionCallback& callback, 334 const std::string& resource_id, 335 const std::string& new_title) 336 : EntryActionRequest(sender, callback), 337 url_generator_(url_generator), 338 resource_id_(resource_id), 339 new_title_(new_title) { 340 DCHECK(!callback.is_null()); 341} 342 343RenameResourceRequest::~RenameResourceRequest() {} 344 345URLFetcher::RequestType RenameResourceRequest::GetRequestType() const { 346 return URLFetcher::PUT; 347} 348 349std::vector<std::string> 350RenameResourceRequest::GetExtraRequestHeaders() const { 351 std::vector<std::string> headers; 352 headers.push_back(util::kIfMatchAllHeader); 353 return headers; 354} 355 356GURL RenameResourceRequest::GetURL() const { 357 return url_generator_.GenerateEditUrl(resource_id_); 358} 359 360bool RenameResourceRequest::GetContentData(std::string* upload_content_type, 361 std::string* upload_content) { 362 upload_content_type->assign("application/atom+xml"); 363 XmlWriter xml_writer; 364 xml_writer.StartWriting(); 365 xml_writer.StartElement("entry"); 366 xml_writer.AddAttribute("xmlns", "http://www.w3.org/2005/Atom"); 367 368 xml_writer.WriteElement("title", new_title_); 369 370 xml_writer.EndElement(); // Ends "entry" element. 371 xml_writer.StopWriting(); 372 upload_content->assign(xml_writer.GetWrittenString()); 373 DVLOG(1) << "RenameResourceRequest data: " << *upload_content_type << ", [" 374 << *upload_content << "]"; 375 return true; 376} 377 378//=========================== AuthorizeAppRequest ========================== 379 380AuthorizeAppRequest::AuthorizeAppRequest( 381 RequestSender* sender, 382 const GDataWapiUrlGenerator& url_generator, 383 const AuthorizeAppCallback& callback, 384 const std::string& resource_id, 385 const std::string& app_id) 386 : GetDataRequest(sender, 387 base::Bind(&ParseOpenLinkAndRun, app_id, callback)), 388 url_generator_(url_generator), 389 resource_id_(resource_id), 390 app_id_(app_id) { 391 DCHECK(!callback.is_null()); 392} 393 394AuthorizeAppRequest::~AuthorizeAppRequest() {} 395 396URLFetcher::RequestType AuthorizeAppRequest::GetRequestType() const { 397 return URLFetcher::PUT; 398} 399 400std::vector<std::string> 401AuthorizeAppRequest::GetExtraRequestHeaders() const { 402 std::vector<std::string> headers; 403 headers.push_back(util::kIfMatchAllHeader); 404 return headers; 405} 406 407bool AuthorizeAppRequest::GetContentData(std::string* upload_content_type, 408 std::string* upload_content) { 409 upload_content_type->assign("application/atom+xml"); 410 XmlWriter xml_writer; 411 xml_writer.StartWriting(); 412 xml_writer.StartElement("entry"); 413 xml_writer.AddAttribute("xmlns", "http://www.w3.org/2005/Atom"); 414 xml_writer.AddAttribute("xmlns:docs", "http://schemas.google.com/docs/2007"); 415 xml_writer.WriteElement("docs:authorizedApp", app_id_); 416 417 xml_writer.EndElement(); // Ends "entry" element. 418 xml_writer.StopWriting(); 419 upload_content->assign(xml_writer.GetWrittenString()); 420 DVLOG(1) << "AuthorizeAppRequest data: " << *upload_content_type << ", [" 421 << *upload_content << "]"; 422 return true; 423} 424 425GURL AuthorizeAppRequest::GetURL() const { 426 return url_generator_.GenerateEditUrl(resource_id_); 427} 428 429//======================= AddResourceToDirectoryRequest ====================== 430 431AddResourceToDirectoryRequest::AddResourceToDirectoryRequest( 432 RequestSender* sender, 433 const GDataWapiUrlGenerator& url_generator, 434 const EntryActionCallback& callback, 435 const std::string& parent_resource_id, 436 const std::string& resource_id) 437 : EntryActionRequest(sender, callback), 438 url_generator_(url_generator), 439 parent_resource_id_(parent_resource_id), 440 resource_id_(resource_id) { 441 DCHECK(!callback.is_null()); 442} 443 444AddResourceToDirectoryRequest::~AddResourceToDirectoryRequest() {} 445 446GURL AddResourceToDirectoryRequest::GetURL() const { 447 return url_generator_.GenerateContentUrl(parent_resource_id_); 448} 449 450URLFetcher::RequestType 451AddResourceToDirectoryRequest::GetRequestType() const { 452 return URLFetcher::POST; 453} 454 455bool AddResourceToDirectoryRequest::GetContentData( 456 std::string* upload_content_type, std::string* upload_content) { 457 upload_content_type->assign("application/atom+xml"); 458 XmlWriter xml_writer; 459 xml_writer.StartWriting(); 460 xml_writer.StartElement("entry"); 461 xml_writer.AddAttribute("xmlns", "http://www.w3.org/2005/Atom"); 462 463 xml_writer.WriteElement( 464 "id", url_generator_.GenerateEditUrlWithoutParams(resource_id_).spec()); 465 466 xml_writer.EndElement(); // Ends "entry" element. 467 xml_writer.StopWriting(); 468 upload_content->assign(xml_writer.GetWrittenString()); 469 DVLOG(1) << "AddResourceToDirectoryRequest data: " << *upload_content_type 470 << ", [" << *upload_content << "]"; 471 return true; 472} 473 474//==================== RemoveResourceFromDirectoryRequest ==================== 475 476RemoveResourceFromDirectoryRequest::RemoveResourceFromDirectoryRequest( 477 RequestSender* sender, 478 const GDataWapiUrlGenerator& url_generator, 479 const EntryActionCallback& callback, 480 const std::string& parent_resource_id, 481 const std::string& document_resource_id) 482 : EntryActionRequest(sender, callback), 483 url_generator_(url_generator), 484 resource_id_(document_resource_id), 485 parent_resource_id_(parent_resource_id) { 486 DCHECK(!callback.is_null()); 487} 488 489RemoveResourceFromDirectoryRequest::~RemoveResourceFromDirectoryRequest() { 490} 491 492GURL RemoveResourceFromDirectoryRequest::GetURL() const { 493 return url_generator_.GenerateResourceUrlForRemoval( 494 parent_resource_id_, resource_id_); 495} 496 497URLFetcher::RequestType 498RemoveResourceFromDirectoryRequest::GetRequestType() const { 499 return URLFetcher::DELETE_REQUEST; 500} 501 502std::vector<std::string> 503RemoveResourceFromDirectoryRequest::GetExtraRequestHeaders() const { 504 std::vector<std::string> headers; 505 headers.push_back(util::kIfMatchAllHeader); 506 return headers; 507} 508 509//======================= InitiateUploadNewFileRequest ======================= 510 511InitiateUploadNewFileRequest::InitiateUploadNewFileRequest( 512 RequestSender* sender, 513 const GDataWapiUrlGenerator& url_generator, 514 const InitiateUploadCallback& callback, 515 const std::string& content_type, 516 int64 content_length, 517 const std::string& parent_resource_id, 518 const std::string& title) 519 : InitiateUploadRequestBase(sender, callback, content_type, content_length), 520 url_generator_(url_generator), 521 parent_resource_id_(parent_resource_id), 522 title_(title) { 523} 524 525InitiateUploadNewFileRequest::~InitiateUploadNewFileRequest() {} 526 527GURL InitiateUploadNewFileRequest::GetURL() const { 528 return url_generator_.GenerateInitiateUploadNewFileUrl(parent_resource_id_); 529} 530 531net::URLFetcher::RequestType 532InitiateUploadNewFileRequest::GetRequestType() const { 533 return net::URLFetcher::POST; 534} 535 536bool InitiateUploadNewFileRequest::GetContentData( 537 std::string* upload_content_type, 538 std::string* upload_content) { 539 upload_content_type->assign("application/atom+xml"); 540 XmlWriter xml_writer; 541 xml_writer.StartWriting(); 542 xml_writer.StartElement("entry"); 543 xml_writer.AddAttribute("xmlns", "http://www.w3.org/2005/Atom"); 544 xml_writer.AddAttribute("xmlns:docs", 545 "http://schemas.google.com/docs/2007"); 546 xml_writer.WriteElement("title", title_); 547 xml_writer.EndElement(); // Ends "entry" element. 548 xml_writer.StopWriting(); 549 upload_content->assign(xml_writer.GetWrittenString()); 550 DVLOG(1) << "InitiateUploadNewFile: " << *upload_content_type << ", [" 551 << *upload_content << "]"; 552 return true; 553} 554 555//===================== InitiateUploadExistingFileRequest ==================== 556 557InitiateUploadExistingFileRequest::InitiateUploadExistingFileRequest( 558 RequestSender* sender, 559 const GDataWapiUrlGenerator& url_generator, 560 const InitiateUploadCallback& callback, 561 const std::string& content_type, 562 int64 content_length, 563 const std::string& resource_id, 564 const std::string& etag) 565 : InitiateUploadRequestBase(sender, callback, content_type, content_length), 566 url_generator_(url_generator), 567 resource_id_(resource_id), 568 etag_(etag) { 569} 570 571InitiateUploadExistingFileRequest::~InitiateUploadExistingFileRequest() {} 572 573GURL InitiateUploadExistingFileRequest::GetURL() const { 574 return url_generator_.GenerateInitiateUploadExistingFileUrl(resource_id_); 575} 576 577net::URLFetcher::RequestType 578InitiateUploadExistingFileRequest::GetRequestType() const { 579 return net::URLFetcher::PUT; 580} 581 582bool InitiateUploadExistingFileRequest::GetContentData( 583 std::string* upload_content_type, 584 std::string* upload_content) { 585 // According to the document there is no need to send the content-type. 586 // However, the server would return 500 server error without the 587 // content-type. 588 // As its workaround, send "text/plain" content-type here. 589 *upload_content_type = "text/plain"; 590 *upload_content = ""; 591 return true; 592} 593 594std::vector<std::string> 595InitiateUploadExistingFileRequest::GetExtraRequestHeaders() const { 596 std::vector<std::string> headers( 597 InitiateUploadRequestBase::GetExtraRequestHeaders()); 598 headers.push_back(util::GenerateIfMatchHeader(etag_)); 599 return headers; 600} 601 602//============================ ResumeUploadRequest =========================== 603 604ResumeUploadRequest::ResumeUploadRequest( 605 RequestSender* sender, 606 const UploadRangeCallback& callback, 607 const ProgressCallback& progress_callback, 608 const GURL& upload_location, 609 int64 start_position, 610 int64 end_position, 611 int64 content_length, 612 const std::string& content_type, 613 const base::FilePath& local_file_path) 614 : ResumeUploadRequestBase(sender, 615 upload_location, 616 start_position, 617 end_position, 618 content_length, 619 content_type, 620 local_file_path), 621 callback_(callback), 622 progress_callback_(progress_callback) { 623 DCHECK(!callback_.is_null()); 624} 625 626ResumeUploadRequest::~ResumeUploadRequest() {} 627 628void ResumeUploadRequest::OnRangeRequestComplete( 629 const UploadRangeResponse& response, scoped_ptr<base::Value> value) { 630 callback_.Run(response, ParseResourceEntry(value.Pass())); 631} 632 633void ResumeUploadRequest::OnURLFetchUploadProgress( 634 const URLFetcher* source, int64 current, int64 total) { 635 if (!progress_callback_.is_null()) 636 progress_callback_.Run(current, total); 637} 638 639//========================== GetUploadStatusRequest ========================== 640 641GetUploadStatusRequest::GetUploadStatusRequest( 642 RequestSender* sender, 643 const UploadRangeCallback& callback, 644 const GURL& upload_url, 645 int64 content_length) 646 : GetUploadStatusRequestBase(sender, upload_url, content_length), 647 callback_(callback) { 648 DCHECK(!callback.is_null()); 649} 650 651GetUploadStatusRequest::~GetUploadStatusRequest() {} 652 653void GetUploadStatusRequest::OnRangeRequestComplete( 654 const UploadRangeResponse& response, scoped_ptr<base::Value> value) { 655 callback_.Run(response, ParseResourceEntry(value.Pass())); 656} 657 658//========================== DownloadFileRequest ========================== 659 660DownloadFileRequest::DownloadFileRequest( 661 RequestSender* sender, 662 const GDataWapiUrlGenerator& url_generator, 663 const DownloadActionCallback& download_action_callback, 664 const GetContentCallback& get_content_callback, 665 const ProgressCallback& progress_callback, 666 const std::string& resource_id, 667 const base::FilePath& output_file_path) 668 : DownloadFileRequestBase( 669 sender, 670 download_action_callback, 671 get_content_callback, 672 progress_callback, 673 url_generator.GenerateDownloadFileUrl(resource_id), 674 output_file_path) { 675} 676 677DownloadFileRequest::~DownloadFileRequest() { 678} 679 680} // namespace google_apis 681