Home | History | Annotate | Download | only in drive
      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/strings/stringprintf.h"
     12 #include "base/task_runner_util.h"
     13 #include "base/values.h"
     14 #include "chrome/browser/drive/drive_api_util.h"
     15 #include "chrome/browser/google_apis/auth_service.h"
     16 #include "chrome/browser/google_apis/drive_api_parser.h"
     17 #include "chrome/browser/google_apis/drive_api_requests.h"
     18 #include "chrome/browser/google_apis/gdata_errorcode.h"
     19 #include "chrome/browser/google_apis/gdata_wapi_parser.h"
     20 #include "chrome/browser/google_apis/gdata_wapi_requests.h"
     21 #include "chrome/browser/google_apis/request_sender.h"
     22 #include "content/public/browser/browser_thread.h"
     23 
     24 using content::BrowserThread;
     25 using google_apis::AppList;
     26 using google_apis::AuthStatusCallback;
     27 using google_apis::AuthorizeAppCallback;
     28 using google_apis::CancelCallback;
     29 using google_apis::ChangeList;
     30 using google_apis::DownloadActionCallback;
     31 using google_apis::EntryActionCallback;
     32 using google_apis::FileList;
     33 using google_apis::FileResource;
     34 using google_apis::GDATA_OTHER_ERROR;
     35 using google_apis::GDATA_PARSE_ERROR;
     36 using google_apis::GDataErrorCode;
     37 using google_apis::GetAboutRequest;
     38 using google_apis::GetAboutResourceCallback;
     39 using google_apis::GetAppListCallback;
     40 using google_apis::GetApplistRequest;
     41 using google_apis::GetChangelistRequest;
     42 using google_apis::GetContentCallback;
     43 using google_apis::GetFileRequest;
     44 using google_apis::GetFilelistRequest;
     45 using google_apis::GetResourceEntryCallback;
     46 using google_apis::GetResourceEntryRequest;
     47 using google_apis::GetResourceListCallback;
     48 using google_apis::GetShareUrlCallback;
     49 using google_apis::HTTP_NOT_IMPLEMENTED;
     50 using google_apis::HTTP_SUCCESS;
     51 using google_apis::InitiateUploadCallback;
     52 using google_apis::Link;
     53 using google_apis::ProgressCallback;
     54 using google_apis::RequestSender;
     55 using google_apis::ResourceEntry;
     56 using google_apis::ResourceList;
     57 using google_apis::UploadRangeCallback;
     58 using google_apis::UploadRangeResponse;
     59 using google_apis::drive::ContinueGetFileListRequest;
     60 using google_apis::drive::CopyResourceRequest;
     61 using google_apis::drive::CreateDirectoryRequest;
     62 using google_apis::drive::DeleteResourceRequest;
     63 using google_apis::drive::DownloadFileRequest;
     64 using google_apis::drive::GetUploadStatusRequest;
     65 using google_apis::drive::InitiateUploadExistingFileRequest;
     66 using google_apis::drive::InitiateUploadNewFileRequest;
     67 using google_apis::drive::InsertResourceRequest;
     68 using google_apis::drive::RenameResourceRequest;
     69 using google_apis::drive::ResumeUploadRequest;
     70 using google_apis::drive::TouchResourceRequest;
     71 using google_apis::drive::TrashResourceRequest;
     72 
     73 namespace drive {
     74 
     75 namespace {
     76 
     77 // OAuth2 scopes for Drive API.
     78 const char kDriveScope[] = "https://www.googleapis.com/auth/drive";
     79 const char kDriveAppsReadonlyScope[] =
     80     "https://www.googleapis.com/auth/drive.apps.readonly";
     81 
     82 // Expected max number of files resources in a http request.
     83 // Be careful not to use something too small because it might overload the
     84 // server. Be careful not to use something too large because it takes longer
     85 // time to fetch the result without UI response.
     86 const int kMaxNumFilesResourcePerRequest = 500;
     87 const int kMaxNumFilesResourcePerRequestForSearch = 50;
     88 
     89 scoped_ptr<ResourceList> ParseChangeListJsonToResourceList(
     90     scoped_ptr<base::Value> value) {
     91   scoped_ptr<ChangeList> change_list(ChangeList::CreateFrom(*value));
     92   if (!change_list) {
     93     return scoped_ptr<ResourceList>();
     94   }
     95 
     96   return ResourceList::CreateFromChangeList(*change_list);
     97 }
     98 
     99 scoped_ptr<ResourceList> ParseFileListJsonToResourceList(
    100     scoped_ptr<base::Value> value) {
    101   scoped_ptr<FileList> file_list(FileList::CreateFrom(*value));
    102   if (!file_list) {
    103     return scoped_ptr<ResourceList>();
    104   }
    105 
    106   return ResourceList::CreateFromFileList(*file_list);
    107 }
    108 
    109 // Parses JSON value representing either ChangeList or FileList into
    110 // ResourceList.
    111 scoped_ptr<ResourceList> ParseResourceListOnBlockingPool(
    112     scoped_ptr<base::Value> value) {
    113   DCHECK(value);
    114 
    115   // Dispatch the parsing based on kind field.
    116   if (ChangeList::HasChangeListKind(*value)) {
    117     return ParseChangeListJsonToResourceList(value.Pass());
    118   }
    119   if (FileList::HasFileListKind(*value)) {
    120     return ParseFileListJsonToResourceList(value.Pass());
    121   }
    122 
    123   // The value type is unknown, so give up to parse and return an error.
    124   return scoped_ptr<ResourceList>();
    125 }
    126 
    127 // Callback invoked when the parsing of resource list is completed,
    128 // regardless whether it is succeeded or not.
    129 void DidParseResourceListOnBlockingPool(
    130     const GetResourceListCallback& callback,
    131     scoped_ptr<ResourceList> resource_list) {
    132   GDataErrorCode error = resource_list ? HTTP_SUCCESS : GDATA_PARSE_ERROR;
    133   callback.Run(error, resource_list.Pass());
    134 }
    135 
    136 // Sends a task to parse the JSON value into ResourceList on blocking pool,
    137 // with a callback which is called when the task is done.
    138 void ParseResourceListOnBlockingPoolAndRun(
    139     scoped_refptr<base::TaskRunner> blocking_task_runner,
    140     const GetResourceListCallback& callback,
    141     GDataErrorCode error,
    142     scoped_ptr<base::Value> value) {
    143   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    144   DCHECK(!callback.is_null());
    145 
    146   if (error != HTTP_SUCCESS) {
    147     // An error occurs, so run callback immediately.
    148     callback.Run(error, scoped_ptr<ResourceList>());
    149     return;
    150   }
    151 
    152   PostTaskAndReplyWithResult(
    153       blocking_task_runner.get(),
    154       FROM_HERE,
    155       base::Bind(&ParseResourceListOnBlockingPool, base::Passed(&value)),
    156       base::Bind(&DidParseResourceListOnBlockingPool, callback));
    157 }
    158 
    159 // Parses the FileResource value to ResourceEntry and runs |callback| on the
    160 // UI thread.
    161 void ParseResourceEntryAndRun(
    162     const GetResourceEntryCallback& callback,
    163     GDataErrorCode error,
    164     scoped_ptr<FileResource> value) {
    165   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    166   DCHECK(!callback.is_null());
    167 
    168   if (!value) {
    169     callback.Run(error, scoped_ptr<ResourceEntry>());
    170     return;
    171   }
    172 
    173   // Converting to ResourceEntry is cheap enough to do on UI thread.
    174   scoped_ptr<ResourceEntry> entry =
    175       ResourceEntry::CreateFromFileResource(*value);
    176   if (!entry) {
    177     callback.Run(GDATA_PARSE_ERROR, scoped_ptr<ResourceEntry>());
    178     return;
    179   }
    180 
    181   callback.Run(error, entry.Pass());
    182 }
    183 
    184 // Parses the JSON value to AppList runs |callback| on the UI thread
    185 // once parsing is done.
    186 void ParseAppListAndRun(const google_apis::GetAppListCallback& callback,
    187                         google_apis::GDataErrorCode error,
    188                         scoped_ptr<base::Value> value) {
    189   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    190   DCHECK(!callback.is_null());
    191 
    192   if (!value) {
    193     callback.Run(error, scoped_ptr<google_apis::AppList>());
    194     return;
    195   }
    196 
    197   // Parsing AppList is cheap enough to do on UI thread.
    198   scoped_ptr<google_apis::AppList> app_list =
    199       google_apis::AppList::CreateFrom(*value);
    200   if (!app_list) {
    201     callback.Run(google_apis::GDATA_PARSE_ERROR,
    202                  scoped_ptr<google_apis::AppList>());
    203     return;
    204   }
    205 
    206   callback.Run(error, app_list.Pass());
    207 }
    208 
    209 // Parses the FileResource value to ResourceEntry for upload range request,
    210 // and runs |callback| on the UI thread.
    211 void ParseResourceEntryForUploadRangeAndRun(
    212     const UploadRangeCallback& callback,
    213     const UploadRangeResponse& response,
    214     scoped_ptr<FileResource> value) {
    215   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    216   DCHECK(!callback.is_null());
    217 
    218   if (!value) {
    219     callback.Run(response, scoped_ptr<ResourceEntry>());
    220     return;
    221   }
    222 
    223   // Converting to ResourceEntry is cheap enough to do on UI thread.
    224   scoped_ptr<ResourceEntry> entry =
    225       ResourceEntry::CreateFromFileResource(*value);
    226   if (!entry) {
    227     callback.Run(UploadRangeResponse(GDATA_PARSE_ERROR,
    228                                      response.start_position_received,
    229                                      response.end_position_received),
    230                  scoped_ptr<ResourceEntry>());
    231     return;
    232   }
    233 
    234   callback.Run(response, entry.Pass());
    235 }
    236 
    237 void ExtractOpenUrlAndRun(const std::string& app_id,
    238                           const AuthorizeAppCallback& callback,
    239                           GDataErrorCode error,
    240                           scoped_ptr<FileResource> value) {
    241   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    242   DCHECK(!callback.is_null());
    243 
    244   if (!value) {
    245     callback.Run(error, GURL());
    246     return;
    247   }
    248 
    249   const std::vector<FileResource::OpenWithLink>& open_with_links =
    250       value->open_with_links();
    251   for (size_t i = 0; i < open_with_links.size(); ++i) {
    252     if (open_with_links[i].app_id == app_id) {
    253       callback.Run(HTTP_SUCCESS, open_with_links[i].open_url);
    254       return;
    255     }
    256   }
    257 
    258   // Not found.
    259   callback.Run(GDATA_OTHER_ERROR, GURL());
    260 }
    261 
    262 // The resource ID for the root directory for Drive API is defined in the spec:
    263 // https://developers.google.com/drive/folder
    264 const char kDriveApiRootDirectoryResourceId[] = "root";
    265 
    266 }  // namespace
    267 
    268 DriveAPIService::DriveAPIService(
    269     OAuth2TokenService* oauth2_token_service,
    270     net::URLRequestContextGetter* url_request_context_getter,
    271     base::TaskRunner* blocking_task_runner,
    272     const GURL& base_url,
    273     const GURL& base_download_url,
    274     const GURL& wapi_base_url,
    275     const std::string& custom_user_agent)
    276     : oauth2_token_service_(oauth2_token_service),
    277       url_request_context_getter_(url_request_context_getter),
    278       blocking_task_runner_(blocking_task_runner),
    279       url_generator_(base_url, base_download_url),
    280       wapi_url_generator_(wapi_base_url, base_download_url),
    281       custom_user_agent_(custom_user_agent) {
    282   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    283 }
    284 
    285 DriveAPIService::~DriveAPIService() {
    286   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    287   if (sender_.get())
    288     sender_->auth_service()->RemoveObserver(this);
    289 }
    290 
    291 void DriveAPIService::Initialize() {
    292   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    293 
    294   std::vector<std::string> scopes;
    295   scopes.push_back(kDriveScope);
    296   scopes.push_back(kDriveAppsReadonlyScope);
    297 
    298   // GData WAPI token. These are for GetShareUrl().
    299   scopes.push_back(util::kDocsListScope);
    300   scopes.push_back(util::kDriveAppsScope);
    301 
    302   sender_.reset(new RequestSender(
    303      new google_apis::AuthService(
    304          oauth2_token_service_, url_request_context_getter_, scopes),
    305      url_request_context_getter_,
    306      blocking_task_runner_.get(),
    307      custom_user_agent_));
    308   sender_->auth_service()->AddObserver(this);
    309 }
    310 
    311 void DriveAPIService::AddObserver(DriveServiceObserver* observer) {
    312   observers_.AddObserver(observer);
    313 }
    314 
    315 void DriveAPIService::RemoveObserver(DriveServiceObserver* observer) {
    316   observers_.RemoveObserver(observer);
    317 }
    318 
    319 bool DriveAPIService::CanSendRequest() const {
    320   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    321 
    322   return HasRefreshToken();
    323 }
    324 
    325 std::string DriveAPIService::CanonicalizeResourceId(
    326     const std::string& resource_id) const {
    327   return drive::util::CanonicalizeResourceId(resource_id);
    328 }
    329 
    330 std::string DriveAPIService::GetRootResourceId() const {
    331   return kDriveApiRootDirectoryResourceId;
    332 }
    333 
    334 CancelCallback DriveAPIService::GetAllResourceList(
    335     const GetResourceListCallback& callback) {
    336   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    337   DCHECK(!callback.is_null());
    338 
    339   // The simplest way to fetch the all resources list looks files.list method,
    340   // but it seems impossible to know the returned list's changestamp.
    341   // Thus, instead, we use changes.list method with includeDeleted=false here.
    342   // The returned list should contain only resources currently existing.
    343   return sender_->StartRequestWithRetry(
    344       new GetChangelistRequest(
    345           sender_.get(),
    346           url_generator_,
    347           false,  // include deleted
    348           0,
    349           kMaxNumFilesResourcePerRequest,
    350           base::Bind(&ParseResourceListOnBlockingPoolAndRun,
    351                      blocking_task_runner_,
    352                      callback)));
    353 }
    354 
    355 CancelCallback DriveAPIService::GetResourceListInDirectory(
    356     const std::string& directory_resource_id,
    357     const GetResourceListCallback& callback) {
    358   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    359   DCHECK(!directory_resource_id.empty());
    360   DCHECK(!callback.is_null());
    361 
    362   // Because children.list method on Drive API v2 returns only the list of
    363   // children's references, but we need all file resource list.
    364   // So, here we use files.list method instead, with setting parents query.
    365   // After the migration from GData WAPI to Drive API v2, we should clean the
    366   // code up by moving the responsibility to include "parents" in the query
    367   // to client side.
    368   // We aren't interested in files in trash in this context, neither.
    369   return sender_->StartRequestWithRetry(
    370       new GetFilelistRequest(
    371           sender_.get(),
    372           url_generator_,
    373           base::StringPrintf(
    374               "'%s' in parents and trashed = false",
    375               drive::util::EscapeQueryStringValue(
    376                   directory_resource_id).c_str()),
    377           kMaxNumFilesResourcePerRequest,
    378           base::Bind(&ParseResourceListOnBlockingPoolAndRun,
    379                      blocking_task_runner_,
    380                      callback)));
    381 }
    382 
    383 CancelCallback DriveAPIService::Search(
    384     const std::string& search_query,
    385     const GetResourceListCallback& callback) {
    386   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    387   DCHECK(!search_query.empty());
    388   DCHECK(!callback.is_null());
    389 
    390   return sender_->StartRequestWithRetry(
    391       new GetFilelistRequest(
    392           sender_.get(),
    393           url_generator_,
    394           drive::util::TranslateQuery(search_query),
    395           kMaxNumFilesResourcePerRequestForSearch,
    396           base::Bind(&ParseResourceListOnBlockingPoolAndRun,
    397                      blocking_task_runner_,
    398                      callback)));
    399 }
    400 
    401 CancelCallback DriveAPIService::SearchByTitle(
    402     const std::string& title,
    403     const std::string& directory_resource_id,
    404     const GetResourceListCallback& callback) {
    405   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    406   DCHECK(!title.empty());
    407   DCHECK(!callback.is_null());
    408 
    409   std::string query;
    410   base::StringAppendF(&query, "title = '%s'",
    411                       drive::util::EscapeQueryStringValue(title).c_str());
    412   if (!directory_resource_id.empty()) {
    413     base::StringAppendF(
    414         &query, " and '%s' in parents",
    415         drive::util::EscapeQueryStringValue(directory_resource_id).c_str());
    416   }
    417   query += " and trashed = false";
    418 
    419   return sender_->StartRequestWithRetry(
    420       new GetFilelistRequest(
    421           sender_.get(),
    422           url_generator_,
    423           query,
    424           kMaxNumFilesResourcePerRequest,
    425           base::Bind(&ParseResourceListOnBlockingPoolAndRun,
    426                      blocking_task_runner_,
    427                      callback)));
    428 }
    429 
    430 CancelCallback DriveAPIService::GetChangeList(
    431     int64 start_changestamp,
    432     const GetResourceListCallback& callback) {
    433   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    434   DCHECK(!callback.is_null());
    435 
    436   return sender_->StartRequestWithRetry(
    437       new GetChangelistRequest(
    438           sender_.get(),
    439           url_generator_,
    440           true,  // include deleted
    441           start_changestamp,
    442           kMaxNumFilesResourcePerRequest,
    443           base::Bind(&ParseResourceListOnBlockingPoolAndRun,
    444                      blocking_task_runner_,
    445                      callback)));
    446 }
    447 
    448 CancelCallback DriveAPIService::ContinueGetResourceList(
    449     const GURL& override_url,
    450     const GetResourceListCallback& callback) {
    451   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    452   DCHECK(!callback.is_null());
    453 
    454   return sender_->StartRequestWithRetry(
    455       new ContinueGetFileListRequest(
    456           sender_.get(),
    457           override_url,
    458           base::Bind(&ParseResourceListOnBlockingPoolAndRun,
    459                      blocking_task_runner_,
    460                      callback)));
    461 }
    462 
    463 CancelCallback DriveAPIService::GetResourceEntry(
    464     const std::string& resource_id,
    465     const GetResourceEntryCallback& callback) {
    466   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    467   DCHECK(!callback.is_null());
    468 
    469   return sender_->StartRequestWithRetry(new GetFileRequest(
    470       sender_.get(),
    471       url_generator_,
    472       resource_id,
    473       base::Bind(&ParseResourceEntryAndRun, callback)));
    474 }
    475 
    476 CancelCallback DriveAPIService::GetShareUrl(
    477     const std::string& resource_id,
    478     const GURL& embed_origin,
    479     const GetShareUrlCallback& callback) {
    480   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    481   DCHECK(!callback.is_null());
    482 
    483   // Unfortunately "share url" is not yet supported on Drive API v2.
    484   // So, as a fallback, we use GData WAPI protocol for this method.
    485   // TODO(hidehiko): Get rid of this implementation when share url is
    486   // supported on Drive API v2.
    487   return sender_->StartRequestWithRetry(
    488       new GetResourceEntryRequest(sender_.get(),
    489                                   wapi_url_generator_,
    490                                   resource_id,
    491                                   embed_origin,
    492                                   base::Bind(&util::ParseShareUrlAndRun,
    493                                              callback)));
    494 }
    495 
    496 CancelCallback DriveAPIService::GetAboutResource(
    497     const GetAboutResourceCallback& callback) {
    498   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    499   DCHECK(!callback.is_null());
    500 
    501   return sender_->StartRequestWithRetry(
    502       new GetAboutRequest(
    503           sender_.get(),
    504           url_generator_,
    505           callback));
    506 }
    507 
    508 CancelCallback DriveAPIService::GetAppList(const GetAppListCallback& callback) {
    509   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    510   DCHECK(!callback.is_null());
    511 
    512   return sender_->StartRequestWithRetry(new GetApplistRequest(
    513       sender_.get(),
    514       url_generator_,
    515       base::Bind(&ParseAppListAndRun, callback)));
    516 }
    517 
    518 CancelCallback DriveAPIService::DownloadFile(
    519     const base::FilePath& local_cache_path,
    520     const std::string& resource_id,
    521     const DownloadActionCallback& download_action_callback,
    522     const GetContentCallback& get_content_callback,
    523     const ProgressCallback& progress_callback) {
    524   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    525   DCHECK(!download_action_callback.is_null());
    526   // get_content_callback may be null.
    527 
    528   return sender_->StartRequestWithRetry(
    529       new DownloadFileRequest(sender_.get(),
    530                               url_generator_,
    531                               resource_id,
    532                               local_cache_path,
    533                               download_action_callback,
    534                               get_content_callback,
    535                               progress_callback));
    536 }
    537 
    538 CancelCallback DriveAPIService::DeleteResource(
    539     const std::string& resource_id,
    540     const std::string& etag,
    541     const EntryActionCallback& callback) {
    542   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    543   DCHECK(!callback.is_null());
    544 
    545   return sender_->StartRequestWithRetry(new TrashResourceRequest(
    546       sender_.get(),
    547       url_generator_,
    548       resource_id,
    549       callback));
    550 }
    551 
    552 CancelCallback DriveAPIService::AddNewDirectory(
    553     const std::string& parent_resource_id,
    554     const std::string& directory_title,
    555     const GetResourceEntryCallback& callback) {
    556   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    557   DCHECK(!callback.is_null());
    558 
    559   return sender_->StartRequestWithRetry(
    560       new CreateDirectoryRequest(
    561           sender_.get(),
    562           url_generator_,
    563           parent_resource_id,
    564           directory_title,
    565           base::Bind(&ParseResourceEntryAndRun, callback)));
    566 }
    567 
    568 CancelCallback DriveAPIService::CopyResource(
    569     const std::string& resource_id,
    570     const std::string& parent_resource_id,
    571     const std::string& new_title,
    572     const GetResourceEntryCallback& callback) {
    573   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    574   DCHECK(!callback.is_null());
    575 
    576   return sender_->StartRequestWithRetry(
    577       new CopyResourceRequest(
    578           sender_.get(),
    579           url_generator_,
    580           resource_id,
    581           parent_resource_id,
    582           new_title,
    583           base::Bind(&ParseResourceEntryAndRun, callback)));
    584 }
    585 
    586 CancelCallback DriveAPIService::CopyHostedDocument(
    587     const std::string& resource_id,
    588     const std::string& new_title,
    589     const GetResourceEntryCallback& callback) {
    590   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    591   DCHECK(!callback.is_null());
    592 
    593   return sender_->StartRequestWithRetry(
    594       new CopyResourceRequest(
    595           sender_.get(),
    596           url_generator_,
    597           resource_id,
    598           std::string(),  // parent_resource_id.
    599           new_title,
    600           base::Bind(&ParseResourceEntryAndRun, callback)));
    601 }
    602 
    603 CancelCallback DriveAPIService::RenameResource(
    604     const std::string& resource_id,
    605     const std::string& new_title,
    606     const EntryActionCallback& callback) {
    607   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    608   DCHECK(!callback.is_null());
    609 
    610   return sender_->StartRequestWithRetry(
    611       new RenameResourceRequest(
    612           sender_.get(),
    613           url_generator_,
    614           resource_id,
    615           new_title,
    616           callback));
    617 }
    618 
    619 CancelCallback DriveAPIService::TouchResource(
    620     const std::string& resource_id,
    621     const base::Time& modified_date,
    622     const base::Time& last_viewed_by_me_date,
    623     const GetResourceEntryCallback& callback) {
    624   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    625   DCHECK(!modified_date.is_null());
    626   DCHECK(!last_viewed_by_me_date.is_null());
    627   DCHECK(!callback.is_null());
    628 
    629   return sender_->StartRequestWithRetry(
    630       new TouchResourceRequest(
    631           sender_.get(),
    632           url_generator_,
    633           resource_id,
    634           modified_date,
    635           last_viewed_by_me_date,
    636           base::Bind(&ParseResourceEntryAndRun, callback)));
    637 }
    638 
    639 CancelCallback DriveAPIService::AddResourceToDirectory(
    640     const std::string& parent_resource_id,
    641     const std::string& resource_id,
    642     const EntryActionCallback& callback) {
    643   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    644   DCHECK(!callback.is_null());
    645 
    646   return sender_->StartRequestWithRetry(
    647       new InsertResourceRequest(
    648           sender_.get(),
    649           url_generator_,
    650           parent_resource_id,
    651           resource_id,
    652           callback));
    653 }
    654 
    655 CancelCallback DriveAPIService::RemoveResourceFromDirectory(
    656     const std::string& parent_resource_id,
    657     const std::string& resource_id,
    658     const EntryActionCallback& callback) {
    659   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    660   DCHECK(!callback.is_null());
    661 
    662   return sender_->StartRequestWithRetry(
    663       new DeleteResourceRequest(
    664           sender_.get(),
    665           url_generator_,
    666           parent_resource_id,
    667           resource_id,
    668           callback));
    669 }
    670 
    671 CancelCallback DriveAPIService::InitiateUploadNewFile(
    672     const std::string& content_type,
    673     int64 content_length,
    674     const std::string& parent_resource_id,
    675     const std::string& title,
    676     const InitiateUploadCallback& callback) {
    677   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    678   DCHECK(!callback.is_null());
    679 
    680   return sender_->StartRequestWithRetry(
    681       new InitiateUploadNewFileRequest(
    682           sender_.get(),
    683           url_generator_,
    684           content_type,
    685           content_length,
    686           parent_resource_id,
    687           title,
    688           callback));
    689 }
    690 
    691 CancelCallback DriveAPIService::InitiateUploadExistingFile(
    692     const std::string& content_type,
    693     int64 content_length,
    694     const std::string& resource_id,
    695     const std::string& etag,
    696     const InitiateUploadCallback& callback) {
    697   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    698   DCHECK(!callback.is_null());
    699 
    700   return sender_->StartRequestWithRetry(
    701       new InitiateUploadExistingFileRequest(
    702           sender_.get(),
    703           url_generator_,
    704           content_type,
    705           content_length,
    706           resource_id,
    707           etag,
    708           callback));
    709 }
    710 
    711 CancelCallback DriveAPIService::ResumeUpload(
    712     const GURL& upload_url,
    713     int64 start_position,
    714     int64 end_position,
    715     int64 content_length,
    716     const std::string& content_type,
    717     const base::FilePath& local_file_path,
    718     const UploadRangeCallback& callback,
    719     const ProgressCallback& progress_callback) {
    720   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    721   DCHECK(!callback.is_null());
    722 
    723   return sender_->StartRequestWithRetry(
    724       new ResumeUploadRequest(
    725           sender_.get(),
    726           upload_url,
    727           start_position,
    728           end_position,
    729           content_length,
    730           content_type,
    731           local_file_path,
    732           base::Bind(&ParseResourceEntryForUploadRangeAndRun, callback),
    733           progress_callback));
    734 }
    735 
    736 CancelCallback DriveAPIService::GetUploadStatus(
    737     const GURL& upload_url,
    738     int64 content_length,
    739     const UploadRangeCallback& callback) {
    740   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    741   DCHECK(!callback.is_null());
    742 
    743   return sender_->StartRequestWithRetry(new GetUploadStatusRequest(
    744       sender_.get(),
    745       upload_url,
    746       content_length,
    747       base::Bind(&ParseResourceEntryForUploadRangeAndRun, callback)));
    748 }
    749 
    750 CancelCallback DriveAPIService::AuthorizeApp(
    751     const std::string& resource_id,
    752     const std::string& app_id,
    753     const AuthorizeAppCallback& callback) {
    754   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    755   DCHECK(!callback.is_null());
    756 
    757   return sender_->StartRequestWithRetry(new GetFileRequest(
    758       sender_.get(),
    759       url_generator_,
    760       resource_id,
    761       base::Bind(&ExtractOpenUrlAndRun, app_id, callback)));
    762 }
    763 
    764 bool DriveAPIService::HasAccessToken() const {
    765   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    766   return sender_->auth_service()->HasAccessToken();
    767 }
    768 
    769 void DriveAPIService::RequestAccessToken(const AuthStatusCallback& callback) {
    770   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    771   DCHECK(!callback.is_null());
    772 
    773   const std::string access_token = sender_->auth_service()->access_token();
    774   if (!access_token.empty()) {
    775     callback.Run(google_apis::HTTP_NOT_MODIFIED, access_token);
    776     return;
    777   }
    778 
    779   // Retrieve the new auth token.
    780   sender_->auth_service()->StartAuthentication(callback);
    781 }
    782 
    783 bool DriveAPIService::HasRefreshToken() const {
    784   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    785   return sender_->auth_service()->HasRefreshToken();
    786 }
    787 
    788 void DriveAPIService::ClearAccessToken() {
    789   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    790   sender_->auth_service()->ClearAccessToken();
    791 }
    792 
    793 void DriveAPIService::ClearRefreshToken() {
    794   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    795   sender_->auth_service()->ClearRefreshToken();
    796 }
    797 
    798 void DriveAPIService::OnOAuth2RefreshTokenChanged() {
    799   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    800   if (CanSendRequest()) {
    801     FOR_EACH_OBSERVER(
    802         DriveServiceObserver, observers_, OnReadyToSendRequests());
    803   } else if (!HasRefreshToken()) {
    804     FOR_EACH_OBSERVER(
    805         DriveServiceObserver, observers_, OnRefreshTokenInvalid());
    806   }
    807 }
    808 
    809 }  // namespace drive
    810