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
24using content::BrowserThread;
25using google_apis::AppList;
26using google_apis::AuthStatusCallback;
27using google_apis::AuthorizeAppCallback;
28using google_apis::CancelCallback;
29using google_apis::ChangeList;
30using google_apis::DownloadActionCallback;
31using google_apis::EntryActionCallback;
32using google_apis::FileList;
33using google_apis::FileResource;
34using google_apis::GDATA_OTHER_ERROR;
35using google_apis::GDATA_PARSE_ERROR;
36using google_apis::GDataErrorCode;
37using google_apis::GetAboutRequest;
38using google_apis::GetAboutResourceCallback;
39using google_apis::GetAppListCallback;
40using google_apis::GetApplistRequest;
41using google_apis::GetChangelistRequest;
42using google_apis::GetContentCallback;
43using google_apis::GetFileRequest;
44using google_apis::GetFilelistRequest;
45using google_apis::GetResourceEntryCallback;
46using google_apis::GetResourceEntryRequest;
47using google_apis::GetResourceListCallback;
48using google_apis::GetShareUrlCallback;
49using google_apis::HTTP_NOT_IMPLEMENTED;
50using google_apis::HTTP_SUCCESS;
51using google_apis::InitiateUploadCallback;
52using google_apis::Link;
53using google_apis::ProgressCallback;
54using google_apis::RequestSender;
55using google_apis::ResourceEntry;
56using google_apis::ResourceList;
57using google_apis::UploadRangeCallback;
58using google_apis::UploadRangeResponse;
59using google_apis::drive::ContinueGetFileListRequest;
60using google_apis::drive::CopyResourceRequest;
61using google_apis::drive::CreateDirectoryRequest;
62using google_apis::drive::DeleteResourceRequest;
63using google_apis::drive::DownloadFileRequest;
64using google_apis::drive::GetUploadStatusRequest;
65using google_apis::drive::InitiateUploadExistingFileRequest;
66using google_apis::drive::InitiateUploadNewFileRequest;
67using google_apis::drive::InsertResourceRequest;
68using google_apis::drive::RenameResourceRequest;
69using google_apis::drive::ResumeUploadRequest;
70using google_apis::drive::TouchResourceRequest;
71using google_apis::drive::TrashResourceRequest;
72
73namespace drive {
74
75namespace {
76
77// OAuth2 scopes for Drive API.
78const char kDriveScope[] = "https://www.googleapis.com/auth/drive";
79const 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.
86const int kMaxNumFilesResourcePerRequest = 500;
87const int kMaxNumFilesResourcePerRequestForSearch = 50;
88
89scoped_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
99scoped_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.
111scoped_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.
129void 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.
138void 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.
161void 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.
186void 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.
211void 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
237void 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
264const char kDriveApiRootDirectoryResourceId[] = "root";
265
266}  // namespace
267
268DriveAPIService::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
285DriveAPIService::~DriveAPIService() {
286  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
287  if (sender_.get())
288    sender_->auth_service()->RemoveObserver(this);
289}
290
291void 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
311void DriveAPIService::AddObserver(DriveServiceObserver* observer) {
312  observers_.AddObserver(observer);
313}
314
315void DriveAPIService::RemoveObserver(DriveServiceObserver* observer) {
316  observers_.RemoveObserver(observer);
317}
318
319bool DriveAPIService::CanSendRequest() const {
320  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
321
322  return HasRefreshToken();
323}
324
325std::string DriveAPIService::CanonicalizeResourceId(
326    const std::string& resource_id) const {
327  return drive::util::CanonicalizeResourceId(resource_id);
328}
329
330std::string DriveAPIService::GetRootResourceId() const {
331  return kDriveApiRootDirectoryResourceId;
332}
333
334CancelCallback 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
355CancelCallback 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
383CancelCallback 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
401CancelCallback 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
430CancelCallback 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
448CancelCallback 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
463CancelCallback 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
476CancelCallback 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
496CancelCallback 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
508CancelCallback 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
518CancelCallback 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
538CancelCallback 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
552CancelCallback 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
568CancelCallback 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
586CancelCallback 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
603CancelCallback 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
619CancelCallback 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
639CancelCallback 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
655CancelCallback 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
671CancelCallback 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
691CancelCallback 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
711CancelCallback 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
736CancelCallback 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
750CancelCallback 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
764bool DriveAPIService::HasAccessToken() const {
765  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
766  return sender_->auth_service()->HasAccessToken();
767}
768
769void 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
783bool DriveAPIService::HasRefreshToken() const {
784  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
785  return sender_->auth_service()->HasRefreshToken();
786}
787
788void DriveAPIService::ClearAccessToken() {
789  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
790  sender_->auth_service()->ClearAccessToken();
791}
792
793void DriveAPIService::ClearRefreshToken() {
794  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
795  sender_->auth_service()->ClearRefreshToken();
796}
797
798void 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