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