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 "chrome/browser/drive/drive_api_service.h" 6 7 #include <string> 8 #include <vector> 9 10 #include "base/bind.h" 11 #include "base/sequenced_task_runner.h" 12 #include "base/strings/stringprintf.h" 13 #include "base/task_runner_util.h" 14 #include "base/values.h" 15 #include "chrome/browser/drive/drive_api_util.h" 16 #include "content/public/browser/browser_thread.h" 17 #include "google_apis/drive/auth_service.h" 18 #include "google_apis/drive/drive_api_parser.h" 19 #include "google_apis/drive/drive_api_requests.h" 20 #include "google_apis/drive/gdata_errorcode.h" 21 #include "google_apis/drive/gdata_wapi_parser.h" 22 #include "google_apis/drive/gdata_wapi_requests.h" 23 #include "google_apis/drive/request_sender.h" 24 #include "net/url_request/url_request_context_getter.h" 25 26 using content::BrowserThread; 27 using google_apis::AppList; 28 using google_apis::AppListCallback; 29 using google_apis::AuthStatusCallback; 30 using google_apis::AuthorizeAppCallback; 31 using google_apis::CancelCallback; 32 using google_apis::ChangeList; 33 using google_apis::DownloadActionCallback; 34 using google_apis::EntryActionCallback; 35 using google_apis::FileList; 36 using google_apis::FileResource; 37 using google_apis::GDATA_OTHER_ERROR; 38 using google_apis::GDATA_PARSE_ERROR; 39 using google_apis::GDataErrorCode; 40 using google_apis::AboutResourceCallback; 41 using google_apis::GetContentCallback; 42 using google_apis::GetResourceEntryCallback; 43 using google_apis::GetResourceEntryRequest; 44 using google_apis::GetResourceListCallback; 45 using google_apis::GetResourceListRequest; 46 using google_apis::GetShareUrlCallback; 47 using google_apis::HTTP_NOT_IMPLEMENTED; 48 using google_apis::HTTP_SUCCESS; 49 using google_apis::InitiateUploadCallback; 50 using google_apis::Link; 51 using google_apis::ProgressCallback; 52 using google_apis::RequestSender; 53 using google_apis::ResourceEntry; 54 using google_apis::ResourceList; 55 using google_apis::UploadRangeCallback; 56 using google_apis::UploadRangeResponse; 57 using google_apis::drive::AboutGetRequest; 58 using google_apis::drive::AppsListRequest; 59 using google_apis::drive::ChangesListRequest; 60 using google_apis::drive::ChangesListNextPageRequest; 61 using google_apis::drive::ChildrenDeleteRequest; 62 using google_apis::drive::ChildrenInsertRequest; 63 using google_apis::drive::DownloadFileRequest; 64 using google_apis::drive::FilesCopyRequest; 65 using google_apis::drive::FilesGetRequest; 66 using google_apis::drive::FilesInsertRequest; 67 using google_apis::drive::FilesPatchRequest; 68 using google_apis::drive::FilesListRequest; 69 using google_apis::drive::FilesListNextPageRequest; 70 using google_apis::drive::FilesDeleteRequest; 71 using google_apis::drive::FilesTrashRequest; 72 using google_apis::drive::GetUploadStatusRequest; 73 using google_apis::drive::InitiateUploadExistingFileRequest; 74 using google_apis::drive::InitiateUploadNewFileRequest; 75 using google_apis::drive::ResumeUploadRequest; 76 77 namespace drive { 78 79 namespace { 80 81 // OAuth2 scopes for Drive API. 82 const char kDriveScope[] = "https://www.googleapis.com/auth/drive"; 83 const char kDriveAppsReadonlyScope[] = 84 "https://www.googleapis.com/auth/drive.apps.readonly"; 85 86 // Mime type to create a directory. 87 const char kFolderMimeType[] = "application/vnd.google-apps.folder"; 88 89 // Max number of file entries to be fetched in a single http request. 90 // 91 // The larger the number is, 92 // - The total running time to fetch the whole file list will become shorter. 93 // - The running time for a single request tends to become longer. 94 // Since the file list fetching is a completely background task, for our side, 95 // only the total time matters. However, the server seems to have a time limit 96 // per single request, which disables us to set the largest value (1000). 97 // TODO(kinaba): make it larger when the server gets faster. 98 const int kMaxNumFilesResourcePerRequest = 250; 99 const int kMaxNumFilesResourcePerRequestForSearch = 50; 100 101 // For performance, we declare all fields we use. 102 const char kAboutResourceFields[] = 103 "kind,quotaBytesTotal,quotaBytesUsed,largestChangeId,rootFolderId"; 104 const char kFileResourceFields[] = 105 "kind,id,title,createdDate,sharedWithMeDate,downloadUrl,mimeType," 106 "md5Checksum,fileSize,labels/trashed,imageMediaMetadata/width," 107 "imageMediaMetadata/height,imageMediaMetadata/rotation,etag," 108 "parents/parentLink,selfLink,thumbnailLink,alternateLink,embedLink," 109 "modifiedDate,lastViewedByMeDate,shared"; 110 const char kFileResourceOpenWithLinksFields[] = 111 "kind,id,openWithLinks/*"; 112 const char kFileListFields[] = 113 "kind,items(kind,id,title,createdDate,sharedWithMeDate,downloadUrl," 114 "mimeType,md5Checksum,fileSize,labels/trashed,imageMediaMetadata/width," 115 "imageMediaMetadata/height,imageMediaMetadata/rotation,etag," 116 "parents/parentLink,selfLink,thumbnailLink,alternateLink,embedLink," 117 "modifiedDate,lastViewedByMeDate,shared),nextLink"; 118 const char kChangeListFields[] = 119 "kind,items(file(kind,id,title,createdDate,sharedWithMeDate,downloadUrl," 120 "mimeType,md5Checksum,fileSize,labels/trashed,imageMediaMetadata/width," 121 "imageMediaMetadata/height,imageMediaMetadata/rotation,etag," 122 "parents/parentLink,selfLink,thumbnailLink,alternateLink,embedLink," 123 "modifiedDate,lastViewedByMeDate,shared),deleted,id,fileId),nextLink," 124 "largestChangeId"; 125 126 // Callback invoked when the parsing of resource list is completed, 127 // regardless whether it is succeeded or not. 128 void DidConvertToResourceListOnBlockingPool( 129 const GetResourceListCallback& callback, 130 scoped_ptr<ResourceList> resource_list) { 131 GDataErrorCode error = resource_list ? HTTP_SUCCESS : GDATA_PARSE_ERROR; 132 callback.Run(error, resource_list.Pass()); 133 } 134 135 // Converts the FileResource value to ResourceEntry and runs |callback| on the 136 // UI thread. 137 void ConvertFileEntryToResourceEntryAndRun( 138 const GetResourceEntryCallback& callback, 139 GDataErrorCode error, 140 scoped_ptr<FileResource> value) { 141 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 142 DCHECK(!callback.is_null()); 143 144 if (!value) { 145 callback.Run(error, scoped_ptr<ResourceEntry>()); 146 return; 147 } 148 149 // Converting to ResourceEntry is cheap enough to do on UI thread. 150 scoped_ptr<ResourceEntry> entry = 151 util::ConvertFileResourceToResourceEntry(*value); 152 if (!entry) { 153 callback.Run(GDATA_PARSE_ERROR, scoped_ptr<ResourceEntry>()); 154 return; 155 } 156 157 callback.Run(error, entry.Pass()); 158 } 159 160 // Thin adapter of ConvertFileListToResourceList. 161 scoped_ptr<ResourceList> ConvertFileListToResourceList( 162 scoped_ptr<FileList> file_list) { 163 return util::ConvertFileListToResourceList(*file_list); 164 } 165 166 // Converts the FileList value to ResourceList on blocking pool and runs 167 // |callback| on the UI thread. 168 void ConvertFileListToResourceListOnBlockingPoolAndRun( 169 scoped_refptr<base::TaskRunner> blocking_task_runner, 170 const GetResourceListCallback& callback, 171 GDataErrorCode error, 172 scoped_ptr<FileList> value) { 173 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 174 DCHECK(!callback.is_null()); 175 176 if (!value) { 177 callback.Run(error, scoped_ptr<ResourceList>()); 178 return; 179 } 180 181 // Convert the value on blocking pool. 182 base::PostTaskAndReplyWithResult( 183 blocking_task_runner.get(), 184 FROM_HERE, 185 base::Bind(&ConvertFileListToResourceList, base::Passed(&value)), 186 base::Bind(&DidConvertToResourceListOnBlockingPool, callback)); 187 } 188 189 // Thin adapter of ConvertChangeListToResourceList. 190 scoped_ptr<ResourceList> ConvertChangeListToResourceList( 191 scoped_ptr<ChangeList> change_list) { 192 return util::ConvertChangeListToResourceList(*change_list); 193 } 194 195 // Converts the FileList value to ResourceList on blocking pool and runs 196 // |callback| on the UI thread. 197 void ConvertChangeListToResourceListOnBlockingPoolAndRun( 198 scoped_refptr<base::TaskRunner> blocking_task_runner, 199 const GetResourceListCallback& callback, 200 GDataErrorCode error, 201 scoped_ptr<ChangeList> value) { 202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 203 DCHECK(!callback.is_null()); 204 205 if (!value) { 206 callback.Run(error, scoped_ptr<ResourceList>()); 207 return; 208 } 209 210 // Convert the value on blocking pool. 211 base::PostTaskAndReplyWithResult( 212 blocking_task_runner.get(), 213 FROM_HERE, 214 base::Bind(&ConvertChangeListToResourceList, base::Passed(&value)), 215 base::Bind(&DidConvertToResourceListOnBlockingPool, callback)); 216 } 217 218 // Converts the FileResource value to ResourceEntry for upload range request, 219 // and runs |callback| on the UI thread. 220 void ConvertFileResourceToResourceEntryForUploadRangeAndRun( 221 const UploadRangeCallback& callback, 222 const UploadRangeResponse& response, 223 scoped_ptr<FileResource> value) { 224 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 225 DCHECK(!callback.is_null()); 226 227 if (!value) { 228 callback.Run(response, scoped_ptr<ResourceEntry>()); 229 return; 230 } 231 232 // Converting to ResourceEntry is cheap enough to do on UI thread. 233 scoped_ptr<ResourceEntry> entry = 234 util::ConvertFileResourceToResourceEntry(*value); 235 if (!entry) { 236 callback.Run(UploadRangeResponse(GDATA_PARSE_ERROR, 237 response.start_position_received, 238 response.end_position_received), 239 scoped_ptr<ResourceEntry>()); 240 return; 241 } 242 243 callback.Run(response, entry.Pass()); 244 } 245 246 void ExtractOpenUrlAndRun(const std::string& app_id, 247 const AuthorizeAppCallback& callback, 248 GDataErrorCode error, 249 scoped_ptr<FileResource> value) { 250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 251 DCHECK(!callback.is_null()); 252 253 if (!value) { 254 callback.Run(error, GURL()); 255 return; 256 } 257 258 const std::vector<FileResource::OpenWithLink>& open_with_links = 259 value->open_with_links(); 260 for (size_t i = 0; i < open_with_links.size(); ++i) { 261 if (open_with_links[i].app_id == app_id) { 262 callback.Run(HTTP_SUCCESS, open_with_links[i].open_url); 263 return; 264 } 265 } 266 267 // Not found. 268 callback.Run(GDATA_OTHER_ERROR, GURL()); 269 } 270 271 // Ignores the |entry|, and runs the |callback|. 272 void EntryActionCallbackAdapter( 273 const EntryActionCallback& callback, 274 GDataErrorCode error, scoped_ptr<FileResource> entry) { 275 callback.Run(error); 276 } 277 278 // The resource ID for the root directory for Drive API is defined in the spec: 279 // https://developers.google.com/drive/folder 280 const char kDriveApiRootDirectoryResourceId[] = "root"; 281 282 } // namespace 283 284 DriveAPIService::DriveAPIService( 285 OAuth2TokenService* oauth2_token_service, 286 net::URLRequestContextGetter* url_request_context_getter, 287 base::SequencedTaskRunner* blocking_task_runner, 288 const GURL& base_url, 289 const GURL& base_download_url, 290 const GURL& wapi_base_url, 291 const std::string& custom_user_agent) 292 : oauth2_token_service_(oauth2_token_service), 293 url_request_context_getter_(url_request_context_getter), 294 blocking_task_runner_(blocking_task_runner), 295 url_generator_(base_url, base_download_url), 296 wapi_url_generator_(wapi_base_url, base_download_url), 297 custom_user_agent_(custom_user_agent) { 298 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 299 } 300 301 DriveAPIService::~DriveAPIService() { 302 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 303 if (sender_.get()) 304 sender_->auth_service()->RemoveObserver(this); 305 } 306 307 void DriveAPIService::Initialize(const std::string& account_id) { 308 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 309 310 std::vector<std::string> scopes; 311 scopes.push_back(kDriveScope); 312 scopes.push_back(kDriveAppsReadonlyScope); 313 314 // GData WAPI token. These are for GetShareUrl(). 315 scopes.push_back(util::kDocsListScope); 316 scopes.push_back(util::kDriveAppsScope); 317 318 sender_.reset(new RequestSender( 319 new google_apis::AuthService(oauth2_token_service_, 320 account_id, 321 url_request_context_getter_.get(), 322 scopes), 323 url_request_context_getter_.get(), 324 blocking_task_runner_.get(), 325 custom_user_agent_)); 326 sender_->auth_service()->AddObserver(this); 327 } 328 329 void DriveAPIService::AddObserver(DriveServiceObserver* observer) { 330 observers_.AddObserver(observer); 331 } 332 333 void DriveAPIService::RemoveObserver(DriveServiceObserver* observer) { 334 observers_.RemoveObserver(observer); 335 } 336 337 bool DriveAPIService::CanSendRequest() const { 338 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 339 340 return HasRefreshToken(); 341 } 342 343 ResourceIdCanonicalizer DriveAPIService::GetResourceIdCanonicalizer() const { 344 return base::Bind(&drive::util::CanonicalizeResourceId); 345 } 346 347 std::string DriveAPIService::GetRootResourceId() const { 348 return kDriveApiRootDirectoryResourceId; 349 } 350 351 CancelCallback DriveAPIService::GetAllResourceList( 352 const GetResourceListCallback& callback) { 353 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 354 DCHECK(!callback.is_null()); 355 356 FilesListRequest* request = new FilesListRequest( 357 sender_.get(), url_generator_, 358 base::Bind(&ConvertFileListToResourceListOnBlockingPoolAndRun, 359 blocking_task_runner_, callback)); 360 request->set_max_results(kMaxNumFilesResourcePerRequest); 361 request->set_q("trashed = false"); // Exclude trashed files. 362 request->set_fields(kFileListFields); 363 return sender_->StartRequestWithRetry(request); 364 } 365 366 CancelCallback DriveAPIService::GetResourceListInDirectory( 367 const std::string& directory_resource_id, 368 const GetResourceListCallback& callback) { 369 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 370 DCHECK(!directory_resource_id.empty()); 371 DCHECK(!callback.is_null()); 372 373 // Because children.list method on Drive API v2 returns only the list of 374 // children's references, but we need all file resource list. 375 // So, here we use files.list method instead, with setting parents query. 376 // After the migration from GData WAPI to Drive API v2, we should clean the 377 // code up by moving the responsibility to include "parents" in the query 378 // to client side. 379 // We aren't interested in files in trash in this context, neither. 380 FilesListRequest* request = new FilesListRequest( 381 sender_.get(), url_generator_, 382 base::Bind(&ConvertFileListToResourceListOnBlockingPoolAndRun, 383 blocking_task_runner_, callback)); 384 request->set_max_results(kMaxNumFilesResourcePerRequest); 385 request->set_q(base::StringPrintf( 386 "'%s' in parents and trashed = false", 387 drive::util::EscapeQueryStringValue(directory_resource_id).c_str())); 388 request->set_fields(kFileListFields); 389 return sender_->StartRequestWithRetry(request); 390 } 391 392 CancelCallback DriveAPIService::Search( 393 const std::string& search_query, 394 const GetResourceListCallback& callback) { 395 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 396 DCHECK(!search_query.empty()); 397 DCHECK(!callback.is_null()); 398 399 FilesListRequest* request = new FilesListRequest( 400 sender_.get(), url_generator_, 401 base::Bind(&ConvertFileListToResourceListOnBlockingPoolAndRun, 402 blocking_task_runner_, callback)); 403 request->set_max_results(kMaxNumFilesResourcePerRequestForSearch); 404 request->set_q(drive::util::TranslateQuery(search_query)); 405 request->set_fields(kFileListFields); 406 return sender_->StartRequestWithRetry(request); 407 } 408 409 CancelCallback DriveAPIService::SearchByTitle( 410 const std::string& title, 411 const std::string& directory_resource_id, 412 const GetResourceListCallback& callback) { 413 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 414 DCHECK(!title.empty()); 415 DCHECK(!callback.is_null()); 416 417 std::string query; 418 base::StringAppendF(&query, "title = '%s'", 419 drive::util::EscapeQueryStringValue(title).c_str()); 420 if (!directory_resource_id.empty()) { 421 base::StringAppendF( 422 &query, " and '%s' in parents", 423 drive::util::EscapeQueryStringValue(directory_resource_id).c_str()); 424 } 425 query += " and trashed = false"; 426 427 FilesListRequest* request = new FilesListRequest( 428 sender_.get(), url_generator_, 429 base::Bind(&ConvertFileListToResourceListOnBlockingPoolAndRun, 430 blocking_task_runner_, callback)); 431 request->set_max_results(kMaxNumFilesResourcePerRequest); 432 request->set_q(query); 433 request->set_fields(kFileListFields); 434 return sender_->StartRequestWithRetry(request); 435 } 436 437 CancelCallback DriveAPIService::GetChangeList( 438 int64 start_changestamp, 439 const GetResourceListCallback& callback) { 440 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 441 DCHECK(!callback.is_null()); 442 443 ChangesListRequest* request = new ChangesListRequest( 444 sender_.get(), url_generator_, 445 base::Bind(&ConvertChangeListToResourceListOnBlockingPoolAndRun, 446 blocking_task_runner_, callback)); 447 request->set_max_results(kMaxNumFilesResourcePerRequest); 448 request->set_start_change_id(start_changestamp); 449 request->set_fields(kChangeListFields); 450 return sender_->StartRequestWithRetry(request); 451 } 452 453 CancelCallback DriveAPIService::GetRemainingChangeList( 454 const GURL& next_link, 455 const GetResourceListCallback& callback) { 456 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 457 DCHECK(!next_link.is_empty()); 458 DCHECK(!callback.is_null()); 459 460 ChangesListNextPageRequest* request = new ChangesListNextPageRequest( 461 sender_.get(), 462 base::Bind(&ConvertChangeListToResourceListOnBlockingPoolAndRun, 463 blocking_task_runner_, callback)); 464 request->set_next_link(next_link); 465 request->set_fields(kChangeListFields); 466 return sender_->StartRequestWithRetry(request); 467 } 468 469 CancelCallback DriveAPIService::GetRemainingFileList( 470 const GURL& next_link, 471 const GetResourceListCallback& callback) { 472 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 473 DCHECK(!next_link.is_empty()); 474 DCHECK(!callback.is_null()); 475 476 FilesListNextPageRequest* request = new FilesListNextPageRequest( 477 sender_.get(), 478 base::Bind(&ConvertFileListToResourceListOnBlockingPoolAndRun, 479 blocking_task_runner_, callback)); 480 request->set_next_link(next_link); 481 request->set_fields(kFileListFields); 482 return sender_->StartRequestWithRetry(request); 483 } 484 485 CancelCallback DriveAPIService::GetResourceEntry( 486 const std::string& resource_id, 487 const GetResourceEntryCallback& callback) { 488 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 489 DCHECK(!callback.is_null()); 490 491 FilesGetRequest* request = new FilesGetRequest( 492 sender_.get(), url_generator_, 493 base::Bind(&ConvertFileEntryToResourceEntryAndRun, callback)); 494 request->set_file_id(resource_id); 495 request->set_fields(kFileResourceFields); 496 return sender_->StartRequestWithRetry(request); 497 } 498 499 CancelCallback DriveAPIService::GetShareUrl( 500 const std::string& resource_id, 501 const GURL& embed_origin, 502 const GetShareUrlCallback& callback) { 503 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 504 DCHECK(!callback.is_null()); 505 506 // Unfortunately "share url" is not yet supported on Drive API v2. 507 // So, as a fallback, we use GData WAPI protocol for this method. 508 // TODO(hidehiko): Get rid of this implementation when share url is 509 // supported on Drive API v2. 510 return sender_->StartRequestWithRetry( 511 new GetResourceEntryRequest(sender_.get(), 512 wapi_url_generator_, 513 resource_id, 514 embed_origin, 515 base::Bind(&util::ParseShareUrlAndRun, 516 callback))); 517 } 518 519 CancelCallback DriveAPIService::GetAboutResource( 520 const AboutResourceCallback& callback) { 521 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 522 DCHECK(!callback.is_null()); 523 524 AboutGetRequest* request = 525 new AboutGetRequest(sender_.get(), url_generator_, callback); 526 request->set_fields(kAboutResourceFields); 527 return sender_->StartRequestWithRetry(request); 528 } 529 530 CancelCallback DriveAPIService::GetAppList(const AppListCallback& callback) { 531 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 532 DCHECK(!callback.is_null()); 533 534 return sender_->StartRequestWithRetry( 535 new AppsListRequest(sender_.get(), url_generator_, callback)); 536 } 537 538 CancelCallback DriveAPIService::DownloadFile( 539 const base::FilePath& local_cache_path, 540 const std::string& resource_id, 541 const DownloadActionCallback& download_action_callback, 542 const GetContentCallback& get_content_callback, 543 const ProgressCallback& progress_callback) { 544 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 545 DCHECK(!download_action_callback.is_null()); 546 // get_content_callback may be null. 547 548 return sender_->StartRequestWithRetry( 549 new DownloadFileRequest(sender_.get(), 550 url_generator_, 551 resource_id, 552 local_cache_path, 553 download_action_callback, 554 get_content_callback, 555 progress_callback)); 556 } 557 558 CancelCallback DriveAPIService::DeleteResource( 559 const std::string& resource_id, 560 const std::string& etag, 561 const EntryActionCallback& callback) { 562 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 563 DCHECK(!callback.is_null()); 564 565 FilesDeleteRequest* request = new FilesDeleteRequest( 566 sender_.get(), url_generator_, callback); 567 request->set_file_id(resource_id); 568 request->set_etag(etag); 569 return sender_->StartRequestWithRetry(request); 570 } 571 572 CancelCallback DriveAPIService::TrashResource( 573 const std::string& resource_id, 574 const EntryActionCallback& callback) { 575 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 576 DCHECK(!callback.is_null()); 577 578 FilesTrashRequest* request = new FilesTrashRequest( 579 sender_.get(), url_generator_, 580 base::Bind(&EntryActionCallbackAdapter, callback)); 581 request->set_file_id(resource_id); 582 request->set_fields(kFileResourceFields); 583 return sender_->StartRequestWithRetry(request); 584 } 585 586 CancelCallback DriveAPIService::AddNewDirectory( 587 const std::string& parent_resource_id, 588 const std::string& directory_title, 589 const GetResourceEntryCallback& callback) { 590 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 591 DCHECK(!callback.is_null()); 592 593 FilesInsertRequest* request = new FilesInsertRequest( 594 sender_.get(), url_generator_, 595 base::Bind(&ConvertFileEntryToResourceEntryAndRun, callback)); 596 request->set_mime_type(kFolderMimeType); 597 request->add_parent(parent_resource_id); 598 request->set_title(directory_title); 599 request->set_fields(kFileResourceFields); 600 return sender_->StartRequestWithRetry(request); 601 } 602 603 CancelCallback DriveAPIService::CopyResource( 604 const std::string& resource_id, 605 const std::string& parent_resource_id, 606 const std::string& new_title, 607 const base::Time& last_modified, 608 const GetResourceEntryCallback& callback) { 609 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 610 DCHECK(!callback.is_null()); 611 612 FilesCopyRequest* request = new FilesCopyRequest( 613 sender_.get(), url_generator_, 614 base::Bind(&ConvertFileEntryToResourceEntryAndRun, callback)); 615 request->set_file_id(resource_id); 616 request->add_parent(parent_resource_id); 617 request->set_title(new_title); 618 request->set_modified_date(last_modified); 619 request->set_fields(kFileResourceFields); 620 return sender_->StartRequestWithRetry(request); 621 } 622 623 CancelCallback DriveAPIService::UpdateResource( 624 const std::string& resource_id, 625 const std::string& parent_resource_id, 626 const std::string& new_title, 627 const base::Time& last_modified, 628 const base::Time& last_viewed_by_me, 629 const GetResourceEntryCallback& callback) { 630 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 631 DCHECK(!callback.is_null()); 632 633 FilesPatchRequest* request = new FilesPatchRequest( 634 sender_.get(), url_generator_, 635 base::Bind(&ConvertFileEntryToResourceEntryAndRun, callback)); 636 request->set_file_id(resource_id); 637 request->set_title(new_title); 638 if (!parent_resource_id.empty()) 639 request->add_parent(parent_resource_id); 640 if (!last_modified.is_null()) { 641 // Need to set setModifiedDate to true to overwrite modifiedDate. 642 request->set_set_modified_date(true); 643 request->set_modified_date(last_modified); 644 } 645 if (!last_viewed_by_me.is_null()) { 646 // Need to set updateViewedDate to false, otherwise the lastViewedByMeDate 647 // will be set to the request time (not the specified time via request). 648 request->set_update_viewed_date(false); 649 request->set_last_viewed_by_me_date(last_viewed_by_me); 650 } 651 request->set_fields(kFileResourceFields); 652 return sender_->StartRequestWithRetry(request); 653 } 654 655 CancelCallback DriveAPIService::RenameResource( 656 const std::string& resource_id, 657 const std::string& new_title, 658 const EntryActionCallback& callback) { 659 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 660 DCHECK(!callback.is_null()); 661 662 FilesPatchRequest* request = new FilesPatchRequest( 663 sender_.get(), url_generator_, 664 base::Bind(&EntryActionCallbackAdapter, callback)); 665 request->set_file_id(resource_id); 666 request->set_title(new_title); 667 request->set_fields(kFileResourceFields); 668 return sender_->StartRequestWithRetry(request); 669 } 670 671 CancelCallback DriveAPIService::AddResourceToDirectory( 672 const std::string& parent_resource_id, 673 const std::string& resource_id, 674 const EntryActionCallback& callback) { 675 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 676 DCHECK(!callback.is_null()); 677 678 ChildrenInsertRequest* request = 679 new ChildrenInsertRequest(sender_.get(), url_generator_, callback); 680 request->set_folder_id(parent_resource_id); 681 request->set_id(resource_id); 682 return sender_->StartRequestWithRetry(request); 683 } 684 685 CancelCallback DriveAPIService::RemoveResourceFromDirectory( 686 const std::string& parent_resource_id, 687 const std::string& resource_id, 688 const EntryActionCallback& callback) { 689 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 690 DCHECK(!callback.is_null()); 691 692 ChildrenDeleteRequest* request = 693 new ChildrenDeleteRequest(sender_.get(), url_generator_, callback); 694 request->set_child_id(resource_id); 695 request->set_folder_id(parent_resource_id); 696 return sender_->StartRequestWithRetry(request); 697 } 698 699 CancelCallback DriveAPIService::InitiateUploadNewFile( 700 const std::string& content_type, 701 int64 content_length, 702 const std::string& parent_resource_id, 703 const std::string& title, 704 const InitiateUploadCallback& callback) { 705 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 706 DCHECK(!callback.is_null()); 707 708 return sender_->StartRequestWithRetry( 709 new InitiateUploadNewFileRequest( 710 sender_.get(), 711 url_generator_, 712 content_type, 713 content_length, 714 parent_resource_id, 715 title, 716 callback)); 717 } 718 719 CancelCallback DriveAPIService::InitiateUploadExistingFile( 720 const std::string& content_type, 721 int64 content_length, 722 const std::string& resource_id, 723 const std::string& etag, 724 const InitiateUploadCallback& callback) { 725 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 726 DCHECK(!callback.is_null()); 727 728 return sender_->StartRequestWithRetry( 729 new InitiateUploadExistingFileRequest( 730 sender_.get(), 731 url_generator_, 732 content_type, 733 content_length, 734 resource_id, 735 etag, 736 callback)); 737 } 738 739 CancelCallback DriveAPIService::ResumeUpload( 740 const GURL& upload_url, 741 int64 start_position, 742 int64 end_position, 743 int64 content_length, 744 const std::string& content_type, 745 const base::FilePath& local_file_path, 746 const UploadRangeCallback& callback, 747 const ProgressCallback& progress_callback) { 748 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 749 DCHECK(!callback.is_null()); 750 751 return sender_->StartRequestWithRetry( 752 new ResumeUploadRequest( 753 sender_.get(), 754 upload_url, 755 start_position, 756 end_position, 757 content_length, 758 content_type, 759 local_file_path, 760 base::Bind(&ConvertFileResourceToResourceEntryForUploadRangeAndRun, 761 callback), 762 progress_callback)); 763 } 764 765 CancelCallback DriveAPIService::GetUploadStatus( 766 const GURL& upload_url, 767 int64 content_length, 768 const UploadRangeCallback& callback) { 769 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 770 DCHECK(!callback.is_null()); 771 772 return sender_->StartRequestWithRetry(new GetUploadStatusRequest( 773 sender_.get(), 774 upload_url, 775 content_length, 776 base::Bind(&ConvertFileResourceToResourceEntryForUploadRangeAndRun, 777 callback))); 778 } 779 780 CancelCallback DriveAPIService::AuthorizeApp( 781 const std::string& resource_id, 782 const std::string& app_id, 783 const AuthorizeAppCallback& callback) { 784 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 785 DCHECK(!callback.is_null()); 786 787 FilesGetRequest* request = new FilesGetRequest( 788 sender_.get(), url_generator_, 789 base::Bind(&ExtractOpenUrlAndRun, app_id, callback)); 790 request->set_file_id(resource_id); 791 request->set_fields(kFileResourceOpenWithLinksFields); 792 return sender_->StartRequestWithRetry(request); 793 } 794 795 CancelCallback DriveAPIService::GetResourceListInDirectoryByWapi( 796 const std::string& directory_resource_id, 797 const google_apis::GetResourceListCallback& callback) { 798 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 799 DCHECK(!directory_resource_id.empty()); 800 DCHECK(!callback.is_null()); 801 802 return sender_->StartRequestWithRetry( 803 new GetResourceListRequest(sender_.get(), 804 wapi_url_generator_, 805 GURL(), // No override url 806 0, // start changestamp 807 std::string(), // empty search query 808 directory_resource_id, 809 callback)); 810 } 811 812 CancelCallback DriveAPIService::GetRemainingResourceList( 813 const GURL& next_link, 814 const google_apis::GetResourceListCallback& callback) { 815 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 816 DCHECK(!next_link.is_empty()); 817 DCHECK(!callback.is_null()); 818 819 return sender_->StartRequestWithRetry( 820 new GetResourceListRequest(sender_.get(), 821 wapi_url_generator_, 822 next_link, 823 0, // start changestamp 824 std::string(), // empty search query 825 std::string(), // no directory resource id 826 callback)); 827 } 828 829 bool DriveAPIService::HasAccessToken() const { 830 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 831 return sender_->auth_service()->HasAccessToken(); 832 } 833 834 void DriveAPIService::RequestAccessToken(const AuthStatusCallback& callback) { 835 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 836 DCHECK(!callback.is_null()); 837 838 const std::string access_token = sender_->auth_service()->access_token(); 839 if (!access_token.empty()) { 840 callback.Run(google_apis::HTTP_NOT_MODIFIED, access_token); 841 return; 842 } 843 844 // Retrieve the new auth token. 845 sender_->auth_service()->StartAuthentication(callback); 846 } 847 848 bool DriveAPIService::HasRefreshToken() const { 849 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 850 return sender_->auth_service()->HasRefreshToken(); 851 } 852 853 void DriveAPIService::ClearAccessToken() { 854 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 855 sender_->auth_service()->ClearAccessToken(); 856 } 857 858 void DriveAPIService::ClearRefreshToken() { 859 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 860 sender_->auth_service()->ClearRefreshToken(); 861 } 862 863 void DriveAPIService::OnOAuth2RefreshTokenChanged() { 864 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 865 if (CanSendRequest()) { 866 FOR_EACH_OBSERVER( 867 DriveServiceObserver, observers_, OnReadyToSendRequests()); 868 } else if (!HasRefreshToken()) { 869 FOR_EACH_OBSERVER( 870 DriveServiceObserver, observers_, OnRefreshTokenInvalid()); 871 } 872 } 873 874 } // namespace drive 875