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