Update V8 to version 4.4.65.
[chromium-blink-merge.git] / google_apis / drive / drive_api_requests.cc
blobdec5adc2208b5233f91f62966c9797368a8ba8c3
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.
5 #include "google_apis/drive/drive_api_requests.h"
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/json/json_writer.h"
10 #include "base/location.h"
11 #include "base/sequenced_task_runner.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_piece.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/task_runner_util.h"
16 #include "base/values.h"
17 #include "google_apis/drive/request_sender.h"
18 #include "google_apis/drive/request_util.h"
19 #include "google_apis/drive/time_util.h"
20 #include "net/base/url_util.h"
21 #include "net/http/http_response_headers.h"
23 namespace google_apis {
24 namespace drive {
25 namespace {
27 // Format of one request in batch uploading request.
28 const char kBatchUploadRequestFormat[] =
29 "%s %s HTTP/1.1\n"
30 "Host: %s\n"
31 "X-Goog-Upload-Protocol: multipart\n"
32 "Content-Type: %s\n"
33 "\n";
35 // Request header for specifying batch upload.
36 const char kBatchUploadHeader[] = "X-Goog-Upload-Protocol: batch";
38 // Content type of HTTP request.
39 const char kHttpContentType[] = "application/http";
41 // Break line in HTTP message.
42 const char kHttpBr[] = "\r\n";
44 // Mime type of multipart mixed.
45 const char kMultipartMixedMimeTypePrefix[] = "multipart/mixed; boundary=";
47 // Parses the JSON value to FileResource instance and runs |callback| on the
48 // UI thread once parsing is done.
49 // This is customized version of ParseJsonAndRun defined above to adapt the
50 // remaining response type.
51 void ParseFileResourceWithUploadRangeAndRun(const UploadRangeCallback& callback,
52 const UploadRangeResponse& response,
53 scoped_ptr<base::Value> value) {
54 DCHECK(!callback.is_null());
56 scoped_ptr<FileResource> file_resource;
57 if (value) {
58 file_resource = FileResource::CreateFrom(*value);
59 if (!file_resource) {
60 callback.Run(
61 UploadRangeResponse(DRIVE_PARSE_ERROR,
62 response.start_position_received,
63 response.end_position_received),
64 scoped_ptr<FileResource>());
65 return;
69 callback.Run(response, file_resource.Pass());
72 // Attaches |properties| to the |request_body| if |properties| is not empty.
73 // |request_body| must not be NULL.
74 void AttachProperties(const Properties& properties,
75 base::DictionaryValue* request_body) {
76 DCHECK(request_body);
77 if (properties.empty())
78 return;
80 base::ListValue* const properties_value = new base::ListValue;
81 for (const auto& property : properties) {
82 base::DictionaryValue* const property_value = new base::DictionaryValue;
83 std::string visibility_as_string;
84 switch (property.visibility()) {
85 case Property::VISIBILITY_PRIVATE:
86 visibility_as_string = "PRIVATE";
87 break;
88 case Property::VISIBILITY_PUBLIC:
89 visibility_as_string = "PUBLIC";
90 break;
92 property_value->SetString("visibility", visibility_as_string);
93 property_value->SetString("key", property.key());
94 property_value->SetString("value", property.value());
95 properties_value->Append(property_value);
97 request_body->Set("properties", properties_value);
100 // Creates metadata JSON string for multipart uploading.
101 // All the values are optional. If the value is empty or null, the value does
102 // not appear in the metadata.
103 std::string CreateMultipartUploadMetadataJson(
104 const std::string& title,
105 const std::string& parent_resource_id,
106 const base::Time& modified_date,
107 const base::Time& last_viewed_by_me_date,
108 const Properties& properties) {
109 base::DictionaryValue root;
110 if (!title.empty())
111 root.SetString("title", title);
113 // Fill parent link.
114 if (!parent_resource_id.empty()) {
115 scoped_ptr<base::ListValue> parents(new base::ListValue);
116 parents->Append(
117 google_apis::util::CreateParentValue(parent_resource_id).release());
118 root.Set("parents", parents.release());
121 if (!modified_date.is_null()) {
122 root.SetString("modifiedDate",
123 google_apis::util::FormatTimeAsString(modified_date));
126 if (!last_viewed_by_me_date.is_null()) {
127 root.SetString("lastViewedByMeDate", google_apis::util::FormatTimeAsString(
128 last_viewed_by_me_date));
131 AttachProperties(properties, &root);
132 std::string json_string;
133 base::JSONWriter::Write(&root, &json_string);
134 return json_string;
137 // Splits |string| into lines by |kHttpBr|.
138 // Each line does not include |kHttpBr|.
139 void SplitIntoLines(const std::string& string,
140 std::vector<base::StringPiece>* output) {
141 const size_t br_size = std::string(kHttpBr).size();
142 std::string::const_iterator it = string.begin();
143 std::vector<base::StringPiece> lines;
144 while (true) {
145 const std::string::const_iterator next_pos =
146 std::search(it, string.end(), kHttpBr, kHttpBr + br_size);
147 lines.push_back(base::StringPiece(it, next_pos));
148 if (next_pos == string.end())
149 break;
150 it = next_pos + br_size;
152 output->swap(lines);
155 // Remove transport padding (spaces and tabs at the end of line) from |piece|.
156 base::StringPiece TrimTransportPadding(const base::StringPiece& piece) {
157 size_t trim_size = 0;
158 while (trim_size < piece.size() &&
159 (piece[piece.size() - 1 - trim_size] == ' ' ||
160 piece[piece.size() - 1 - trim_size] == '\t')) {
161 ++trim_size;
163 return piece.substr(0, piece.size() - trim_size);
166 } // namespace
168 MultipartHttpResponse::MultipartHttpResponse() : code(HTTP_SUCCESS) {
171 MultipartHttpResponse::~MultipartHttpResponse() {
174 // The |response| must be multipart/mixed format that contains child HTTP
175 // response of drive batch request.
176 // https://www.ietf.org/rfc/rfc2046.txt
178 // It looks like:
179 // --Boundary
180 // Content-type: application/http
182 // HTTP/1.1 200 OK
183 // Header of child response
185 // Body of child response
186 // --Boundary
187 // Content-type: application/http
189 // HTTP/1.1 404 Not Found
190 // Header of child response
192 // Body of child response
193 // --Boundary--
194 bool ParseMultipartResponse(const std::string& content_type,
195 const std::string& response,
196 std::vector<MultipartHttpResponse>* parts) {
197 if (response.empty())
198 return false;
200 base::StringPiece content_type_piece(content_type);
201 if (!content_type_piece.starts_with(kMultipartMixedMimeTypePrefix)) {
202 return false;
204 content_type_piece.remove_prefix(
205 base::StringPiece(kMultipartMixedMimeTypePrefix).size());
207 if (content_type_piece.empty())
208 return false;
209 if (content_type_piece[0] == '"') {
210 if (content_type_piece.size() <= 2 ||
211 content_type_piece[content_type_piece.size() - 1] != '"') {
212 return false;
214 content_type_piece =
215 content_type_piece.substr(1, content_type_piece.size() - 2);
218 std::string boundary;
219 content_type_piece.CopyToString(&boundary);
220 const std::string header = "--" + boundary;
221 const std::string terminator = "--" + boundary + "--";
223 std::vector<base::StringPiece> lines;
224 SplitIntoLines(response, &lines);
226 enum {
227 STATE_START,
228 STATE_PART_HEADER,
229 STATE_PART_HTTP_STATUS_LINE,
230 STATE_PART_HTTP_HEADER,
231 STATE_PART_HTTP_BODY
232 } state = STATE_START;
234 const std::string kHttpStatusPrefix = "HTTP/1.1 ";
235 std::vector<MultipartHttpResponse> responses;
236 DriveApiErrorCode code = DRIVE_PARSE_ERROR;
237 std::string body;
238 for (const auto& line : lines) {
239 if (state == STATE_PART_HEADER && line.empty()) {
240 state = STATE_PART_HTTP_STATUS_LINE;
241 continue;
244 if (state == STATE_PART_HTTP_STATUS_LINE) {
245 if (line.starts_with(kHttpStatusPrefix)) {
246 int int_code;
247 base::StringToInt(
248 line.substr(base::StringPiece(kHttpStatusPrefix).size()),
249 &int_code);
250 if (int_code > 0)
251 code = static_cast<DriveApiErrorCode>(int_code);
252 else
253 code = DRIVE_PARSE_ERROR;
254 } else {
255 code = DRIVE_PARSE_ERROR;
257 state = STATE_PART_HTTP_HEADER;
258 continue;
261 if (state == STATE_PART_HTTP_HEADER && line.empty()) {
262 state = STATE_PART_HTTP_BODY;
263 body.clear();
264 continue;
266 const base::StringPiece chopped_line = TrimTransportPadding(line);
267 const bool is_new_part = chopped_line == header;
268 const bool was_last_part = chopped_line == terminator;
269 if (is_new_part || was_last_part) {
270 switch (state) {
271 case STATE_START:
272 break;
273 case STATE_PART_HEADER:
274 case STATE_PART_HTTP_STATUS_LINE:
275 responses.push_back(MultipartHttpResponse());
276 responses.back().code = DRIVE_PARSE_ERROR;
277 break;
278 case STATE_PART_HTTP_HEADER:
279 responses.push_back(MultipartHttpResponse());
280 responses.back().code = code;
281 break;
282 case STATE_PART_HTTP_BODY:
283 // Drop the last kHttpBr.
284 if (!body.empty())
285 body.resize(body.size() - 2);
286 responses.push_back(MultipartHttpResponse());
287 responses.back().code = code;
288 responses.back().body.swap(body);
289 break;
291 if (is_new_part)
292 state = STATE_PART_HEADER;
293 if (was_last_part)
294 break;
295 } else if (state == STATE_PART_HTTP_BODY) {
296 line.AppendToString(&body);
297 body.append(kHttpBr);
301 parts->swap(responses);
302 return true;
305 Property::Property() : visibility_(VISIBILITY_PRIVATE) {
308 Property::~Property() {
311 //============================ DriveApiPartialFieldRequest ====================
313 DriveApiPartialFieldRequest::DriveApiPartialFieldRequest(
314 RequestSender* sender) : UrlFetchRequestBase(sender) {
317 DriveApiPartialFieldRequest::~DriveApiPartialFieldRequest() {
320 GURL DriveApiPartialFieldRequest::GetURL() const {
321 GURL url = GetURLInternal();
322 if (!fields_.empty())
323 url = net::AppendOrReplaceQueryParameter(url, "fields", fields_);
324 return url;
327 //=============================== FilesGetRequest =============================
329 FilesGetRequest::FilesGetRequest(
330 RequestSender* sender,
331 const DriveApiUrlGenerator& url_generator,
332 bool use_internal_endpoint,
333 const FileResourceCallback& callback)
334 : DriveApiDataRequest<FileResource>(sender, callback),
335 url_generator_(url_generator),
336 use_internal_endpoint_(use_internal_endpoint) {
337 DCHECK(!callback.is_null());
340 FilesGetRequest::~FilesGetRequest() {}
342 GURL FilesGetRequest::GetURLInternal() const {
343 return url_generator_.GetFilesGetUrl(file_id_,
344 use_internal_endpoint_,
345 embed_origin_);
348 //============================ FilesAuthorizeRequest ===========================
350 FilesAuthorizeRequest::FilesAuthorizeRequest(
351 RequestSender* sender,
352 const DriveApiUrlGenerator& url_generator,
353 const FileResourceCallback& callback)
354 : DriveApiDataRequest<FileResource>(sender, callback),
355 url_generator_(url_generator) {
356 DCHECK(!callback.is_null());
359 FilesAuthorizeRequest::~FilesAuthorizeRequest() {}
361 net::URLFetcher::RequestType FilesAuthorizeRequest::GetRequestType() const {
362 return net::URLFetcher::POST;
365 GURL FilesAuthorizeRequest::GetURLInternal() const {
366 return url_generator_.GetFilesAuthorizeUrl(file_id_, app_id_);
369 //============================ FilesInsertRequest ============================
371 FilesInsertRequest::FilesInsertRequest(
372 RequestSender* sender,
373 const DriveApiUrlGenerator& url_generator,
374 const FileResourceCallback& callback)
375 : DriveApiDataRequest<FileResource>(sender, callback),
376 url_generator_(url_generator) {
377 DCHECK(!callback.is_null());
380 FilesInsertRequest::~FilesInsertRequest() {}
382 net::URLFetcher::RequestType FilesInsertRequest::GetRequestType() const {
383 return net::URLFetcher::POST;
386 bool FilesInsertRequest::GetContentData(std::string* upload_content_type,
387 std::string* upload_content) {
388 *upload_content_type = util::kContentTypeApplicationJson;
390 base::DictionaryValue root;
392 if (!last_viewed_by_me_date_.is_null()) {
393 root.SetString("lastViewedByMeDate",
394 util::FormatTimeAsString(last_viewed_by_me_date_));
397 if (!mime_type_.empty())
398 root.SetString("mimeType", mime_type_);
400 if (!modified_date_.is_null())
401 root.SetString("modifiedDate", util::FormatTimeAsString(modified_date_));
403 if (!parents_.empty()) {
404 base::ListValue* parents_value = new base::ListValue;
405 for (size_t i = 0; i < parents_.size(); ++i) {
406 base::DictionaryValue* parent = new base::DictionaryValue;
407 parent->SetString("id", parents_[i]);
408 parents_value->Append(parent);
410 root.Set("parents", parents_value);
413 if (!title_.empty())
414 root.SetString("title", title_);
416 AttachProperties(properties_, &root);
417 base::JSONWriter::Write(&root, upload_content);
419 DVLOG(1) << "FilesInsert data: " << *upload_content_type << ", ["
420 << *upload_content << "]";
421 return true;
424 GURL FilesInsertRequest::GetURLInternal() const {
425 return url_generator_.GetFilesInsertUrl();
428 //============================== FilesPatchRequest ============================
430 FilesPatchRequest::FilesPatchRequest(
431 RequestSender* sender,
432 const DriveApiUrlGenerator& url_generator,
433 const FileResourceCallback& callback)
434 : DriveApiDataRequest<FileResource>(sender, callback),
435 url_generator_(url_generator),
436 set_modified_date_(false),
437 update_viewed_date_(true) {
438 DCHECK(!callback.is_null());
441 FilesPatchRequest::~FilesPatchRequest() {}
443 net::URLFetcher::RequestType FilesPatchRequest::GetRequestType() const {
444 return net::URLFetcher::PATCH;
447 std::vector<std::string> FilesPatchRequest::GetExtraRequestHeaders() const {
448 std::vector<std::string> headers;
449 headers.push_back(util::kIfMatchAllHeader);
450 return headers;
453 GURL FilesPatchRequest::GetURLInternal() const {
454 return url_generator_.GetFilesPatchUrl(
455 file_id_, set_modified_date_, update_viewed_date_);
458 bool FilesPatchRequest::GetContentData(std::string* upload_content_type,
459 std::string* upload_content) {
460 if (title_.empty() &&
461 modified_date_.is_null() &&
462 last_viewed_by_me_date_.is_null() &&
463 parents_.empty())
464 return false;
466 *upload_content_type = util::kContentTypeApplicationJson;
468 base::DictionaryValue root;
469 if (!title_.empty())
470 root.SetString("title", title_);
472 if (!modified_date_.is_null())
473 root.SetString("modifiedDate", util::FormatTimeAsString(modified_date_));
475 if (!last_viewed_by_me_date_.is_null()) {
476 root.SetString("lastViewedByMeDate",
477 util::FormatTimeAsString(last_viewed_by_me_date_));
480 if (!parents_.empty()) {
481 base::ListValue* parents_value = new base::ListValue;
482 for (size_t i = 0; i < parents_.size(); ++i) {
483 base::DictionaryValue* parent = new base::DictionaryValue;
484 parent->SetString("id", parents_[i]);
485 parents_value->Append(parent);
487 root.Set("parents", parents_value);
490 AttachProperties(properties_, &root);
491 base::JSONWriter::Write(&root, upload_content);
493 DVLOG(1) << "FilesPatch data: " << *upload_content_type << ", ["
494 << *upload_content << "]";
495 return true;
498 //============================= FilesCopyRequest ==============================
500 FilesCopyRequest::FilesCopyRequest(
501 RequestSender* sender,
502 const DriveApiUrlGenerator& url_generator,
503 const FileResourceCallback& callback)
504 : DriveApiDataRequest<FileResource>(sender, callback),
505 url_generator_(url_generator) {
506 DCHECK(!callback.is_null());
509 FilesCopyRequest::~FilesCopyRequest() {
512 net::URLFetcher::RequestType FilesCopyRequest::GetRequestType() const {
513 return net::URLFetcher::POST;
516 GURL FilesCopyRequest::GetURLInternal() const {
517 return url_generator_.GetFilesCopyUrl(file_id_);
520 bool FilesCopyRequest::GetContentData(std::string* upload_content_type,
521 std::string* upload_content) {
522 if (parents_.empty() && title_.empty())
523 return false;
525 *upload_content_type = util::kContentTypeApplicationJson;
527 base::DictionaryValue root;
529 if (!modified_date_.is_null())
530 root.SetString("modifiedDate", util::FormatTimeAsString(modified_date_));
532 if (!parents_.empty()) {
533 base::ListValue* parents_value = new base::ListValue;
534 for (size_t i = 0; i < parents_.size(); ++i) {
535 base::DictionaryValue* parent = new base::DictionaryValue;
536 parent->SetString("id", parents_[i]);
537 parents_value->Append(parent);
539 root.Set("parents", parents_value);
542 if (!title_.empty())
543 root.SetString("title", title_);
545 base::JSONWriter::Write(&root, upload_content);
546 DVLOG(1) << "FilesCopy data: " << *upload_content_type << ", ["
547 << *upload_content << "]";
548 return true;
551 //============================= FilesListRequest =============================
553 FilesListRequest::FilesListRequest(
554 RequestSender* sender,
555 const DriveApiUrlGenerator& url_generator,
556 const FileListCallback& callback)
557 : DriveApiDataRequest<FileList>(sender, callback),
558 url_generator_(url_generator),
559 max_results_(100) {
560 DCHECK(!callback.is_null());
563 FilesListRequest::~FilesListRequest() {}
565 GURL FilesListRequest::GetURLInternal() const {
566 return url_generator_.GetFilesListUrl(max_results_, page_token_, q_);
569 //======================== FilesListNextPageRequest =========================
571 FilesListNextPageRequest::FilesListNextPageRequest(
572 RequestSender* sender,
573 const FileListCallback& callback)
574 : DriveApiDataRequest<FileList>(sender, callback) {
575 DCHECK(!callback.is_null());
578 FilesListNextPageRequest::~FilesListNextPageRequest() {
581 GURL FilesListNextPageRequest::GetURLInternal() const {
582 return next_link_;
585 //============================ FilesDeleteRequest =============================
587 FilesDeleteRequest::FilesDeleteRequest(
588 RequestSender* sender,
589 const DriveApiUrlGenerator& url_generator,
590 const EntryActionCallback& callback)
591 : EntryActionRequest(sender, callback),
592 url_generator_(url_generator) {
593 DCHECK(!callback.is_null());
596 FilesDeleteRequest::~FilesDeleteRequest() {}
598 net::URLFetcher::RequestType FilesDeleteRequest::GetRequestType() const {
599 return net::URLFetcher::DELETE_REQUEST;
602 GURL FilesDeleteRequest::GetURL() const {
603 return url_generator_.GetFilesDeleteUrl(file_id_);
606 std::vector<std::string> FilesDeleteRequest::GetExtraRequestHeaders() const {
607 std::vector<std::string> headers(
608 EntryActionRequest::GetExtraRequestHeaders());
609 headers.push_back(util::GenerateIfMatchHeader(etag_));
610 return headers;
613 //============================ FilesTrashRequest =============================
615 FilesTrashRequest::FilesTrashRequest(
616 RequestSender* sender,
617 const DriveApiUrlGenerator& url_generator,
618 const FileResourceCallback& callback)
619 : DriveApiDataRequest<FileResource>(sender, callback),
620 url_generator_(url_generator) {
621 DCHECK(!callback.is_null());
624 FilesTrashRequest::~FilesTrashRequest() {}
626 net::URLFetcher::RequestType FilesTrashRequest::GetRequestType() const {
627 return net::URLFetcher::POST;
630 GURL FilesTrashRequest::GetURLInternal() const {
631 return url_generator_.GetFilesTrashUrl(file_id_);
634 //============================== AboutGetRequest =============================
636 AboutGetRequest::AboutGetRequest(
637 RequestSender* sender,
638 const DriveApiUrlGenerator& url_generator,
639 const AboutResourceCallback& callback)
640 : DriveApiDataRequest<AboutResource>(sender, callback),
641 url_generator_(url_generator) {
642 DCHECK(!callback.is_null());
645 AboutGetRequest::~AboutGetRequest() {}
647 GURL AboutGetRequest::GetURLInternal() const {
648 return url_generator_.GetAboutGetUrl();
651 //============================ ChangesListRequest ===========================
653 ChangesListRequest::ChangesListRequest(
654 RequestSender* sender,
655 const DriveApiUrlGenerator& url_generator,
656 const ChangeListCallback& callback)
657 : DriveApiDataRequest<ChangeList>(sender, callback),
658 url_generator_(url_generator),
659 include_deleted_(true),
660 max_results_(100),
661 start_change_id_(0) {
662 DCHECK(!callback.is_null());
665 ChangesListRequest::~ChangesListRequest() {}
667 GURL ChangesListRequest::GetURLInternal() const {
668 return url_generator_.GetChangesListUrl(
669 include_deleted_, max_results_, page_token_, start_change_id_);
672 //======================== ChangesListNextPageRequest =========================
674 ChangesListNextPageRequest::ChangesListNextPageRequest(
675 RequestSender* sender,
676 const ChangeListCallback& callback)
677 : DriveApiDataRequest<ChangeList>(sender, callback) {
678 DCHECK(!callback.is_null());
681 ChangesListNextPageRequest::~ChangesListNextPageRequest() {
684 GURL ChangesListNextPageRequest::GetURLInternal() const {
685 return next_link_;
688 //============================== AppsListRequest ===========================
690 AppsListRequest::AppsListRequest(
691 RequestSender* sender,
692 const DriveApiUrlGenerator& url_generator,
693 bool use_internal_endpoint,
694 const AppListCallback& callback)
695 : DriveApiDataRequest<AppList>(sender, callback),
696 url_generator_(url_generator),
697 use_internal_endpoint_(use_internal_endpoint) {
698 DCHECK(!callback.is_null());
701 AppsListRequest::~AppsListRequest() {}
703 GURL AppsListRequest::GetURLInternal() const {
704 return url_generator_.GetAppsListUrl(use_internal_endpoint_);
707 //============================== AppsDeleteRequest ===========================
709 AppsDeleteRequest::AppsDeleteRequest(RequestSender* sender,
710 const DriveApiUrlGenerator& url_generator,
711 const EntryActionCallback& callback)
712 : EntryActionRequest(sender, callback),
713 url_generator_(url_generator) {
714 DCHECK(!callback.is_null());
717 AppsDeleteRequest::~AppsDeleteRequest() {}
719 net::URLFetcher::RequestType AppsDeleteRequest::GetRequestType() const {
720 return net::URLFetcher::DELETE_REQUEST;
723 GURL AppsDeleteRequest::GetURL() const {
724 return url_generator_.GetAppsDeleteUrl(app_id_);
727 //========================== ChildrenInsertRequest ============================
729 ChildrenInsertRequest::ChildrenInsertRequest(
730 RequestSender* sender,
731 const DriveApiUrlGenerator& url_generator,
732 const EntryActionCallback& callback)
733 : EntryActionRequest(sender, callback),
734 url_generator_(url_generator) {
735 DCHECK(!callback.is_null());
738 ChildrenInsertRequest::~ChildrenInsertRequest() {}
740 net::URLFetcher::RequestType ChildrenInsertRequest::GetRequestType() const {
741 return net::URLFetcher::POST;
744 GURL ChildrenInsertRequest::GetURL() const {
745 return url_generator_.GetChildrenInsertUrl(folder_id_);
748 bool ChildrenInsertRequest::GetContentData(std::string* upload_content_type,
749 std::string* upload_content) {
750 *upload_content_type = util::kContentTypeApplicationJson;
752 base::DictionaryValue root;
753 root.SetString("id", id_);
755 base::JSONWriter::Write(&root, upload_content);
756 DVLOG(1) << "InsertResource data: " << *upload_content_type << ", ["
757 << *upload_content << "]";
758 return true;
761 //========================== ChildrenDeleteRequest ============================
763 ChildrenDeleteRequest::ChildrenDeleteRequest(
764 RequestSender* sender,
765 const DriveApiUrlGenerator& url_generator,
766 const EntryActionCallback& callback)
767 : EntryActionRequest(sender, callback),
768 url_generator_(url_generator) {
769 DCHECK(!callback.is_null());
772 ChildrenDeleteRequest::~ChildrenDeleteRequest() {}
774 net::URLFetcher::RequestType ChildrenDeleteRequest::GetRequestType() const {
775 return net::URLFetcher::DELETE_REQUEST;
778 GURL ChildrenDeleteRequest::GetURL() const {
779 return url_generator_.GetChildrenDeleteUrl(child_id_, folder_id_);
782 //======================= InitiateUploadNewFileRequest =======================
784 InitiateUploadNewFileRequest::InitiateUploadNewFileRequest(
785 RequestSender* sender,
786 const DriveApiUrlGenerator& url_generator,
787 const std::string& content_type,
788 int64 content_length,
789 const std::string& parent_resource_id,
790 const std::string& title,
791 const InitiateUploadCallback& callback)
792 : InitiateUploadRequestBase(sender,
793 callback,
794 content_type,
795 content_length),
796 url_generator_(url_generator),
797 parent_resource_id_(parent_resource_id),
798 title_(title) {
801 InitiateUploadNewFileRequest::~InitiateUploadNewFileRequest() {}
803 GURL InitiateUploadNewFileRequest::GetURL() const {
804 return url_generator_.GetInitiateUploadNewFileUrl(!modified_date_.is_null());
807 net::URLFetcher::RequestType
808 InitiateUploadNewFileRequest::GetRequestType() const {
809 return net::URLFetcher::POST;
812 bool InitiateUploadNewFileRequest::GetContentData(
813 std::string* upload_content_type,
814 std::string* upload_content) {
815 *upload_content_type = util::kContentTypeApplicationJson;
817 base::DictionaryValue root;
818 root.SetString("title", title_);
820 // Fill parent link.
821 scoped_ptr<base::ListValue> parents(new base::ListValue);
822 parents->Append(util::CreateParentValue(parent_resource_id_).release());
823 root.Set("parents", parents.release());
825 if (!modified_date_.is_null())
826 root.SetString("modifiedDate", util::FormatTimeAsString(modified_date_));
828 if (!last_viewed_by_me_date_.is_null()) {
829 root.SetString("lastViewedByMeDate",
830 util::FormatTimeAsString(last_viewed_by_me_date_));
833 AttachProperties(properties_, &root);
834 base::JSONWriter::Write(&root, upload_content);
836 DVLOG(1) << "InitiateUploadNewFile data: " << *upload_content_type << ", ["
837 << *upload_content << "]";
838 return true;
841 //===================== InitiateUploadExistingFileRequest ====================
843 InitiateUploadExistingFileRequest::InitiateUploadExistingFileRequest(
844 RequestSender* sender,
845 const DriveApiUrlGenerator& url_generator,
846 const std::string& content_type,
847 int64 content_length,
848 const std::string& resource_id,
849 const std::string& etag,
850 const InitiateUploadCallback& callback)
851 : InitiateUploadRequestBase(sender,
852 callback,
853 content_type,
854 content_length),
855 url_generator_(url_generator),
856 resource_id_(resource_id),
857 etag_(etag) {
860 InitiateUploadExistingFileRequest::~InitiateUploadExistingFileRequest() {}
862 GURL InitiateUploadExistingFileRequest::GetURL() const {
863 return url_generator_.GetInitiateUploadExistingFileUrl(
864 resource_id_, !modified_date_.is_null());
867 net::URLFetcher::RequestType
868 InitiateUploadExistingFileRequest::GetRequestType() const {
869 return net::URLFetcher::PUT;
872 std::vector<std::string>
873 InitiateUploadExistingFileRequest::GetExtraRequestHeaders() const {
874 std::vector<std::string> headers(
875 InitiateUploadRequestBase::GetExtraRequestHeaders());
876 headers.push_back(util::GenerateIfMatchHeader(etag_));
877 return headers;
880 bool InitiateUploadExistingFileRequest::GetContentData(
881 std::string* upload_content_type,
882 std::string* upload_content) {
883 base::DictionaryValue root;
884 if (!parent_resource_id_.empty()) {
885 scoped_ptr<base::ListValue> parents(new base::ListValue);
886 parents->Append(util::CreateParentValue(parent_resource_id_).release());
887 root.Set("parents", parents.release());
890 if (!title_.empty())
891 root.SetString("title", title_);
893 if (!modified_date_.is_null())
894 root.SetString("modifiedDate", util::FormatTimeAsString(modified_date_));
896 if (!last_viewed_by_me_date_.is_null()) {
897 root.SetString("lastViewedByMeDate",
898 util::FormatTimeAsString(last_viewed_by_me_date_));
901 AttachProperties(properties_, &root);
902 if (root.empty())
903 return false;
905 *upload_content_type = util::kContentTypeApplicationJson;
906 base::JSONWriter::Write(&root, upload_content);
907 DVLOG(1) << "InitiateUploadExistingFile data: " << *upload_content_type
908 << ", [" << *upload_content << "]";
909 return true;
912 //============================ ResumeUploadRequest ===========================
914 ResumeUploadRequest::ResumeUploadRequest(
915 RequestSender* sender,
916 const GURL& upload_location,
917 int64 start_position,
918 int64 end_position,
919 int64 content_length,
920 const std::string& content_type,
921 const base::FilePath& local_file_path,
922 const UploadRangeCallback& callback,
923 const ProgressCallback& progress_callback)
924 : ResumeUploadRequestBase(sender,
925 upload_location,
926 start_position,
927 end_position,
928 content_length,
929 content_type,
930 local_file_path),
931 callback_(callback),
932 progress_callback_(progress_callback) {
933 DCHECK(!callback_.is_null());
936 ResumeUploadRequest::~ResumeUploadRequest() {}
938 void ResumeUploadRequest::OnRangeRequestComplete(
939 const UploadRangeResponse& response,
940 scoped_ptr<base::Value> value) {
941 DCHECK(CalledOnValidThread());
942 ParseFileResourceWithUploadRangeAndRun(callback_, response, value.Pass());
945 void ResumeUploadRequest::OnURLFetchUploadProgress(
946 const net::URLFetcher* source, int64 current, int64 total) {
947 if (!progress_callback_.is_null())
948 progress_callback_.Run(current, total);
951 //========================== GetUploadStatusRequest ==========================
953 GetUploadStatusRequest::GetUploadStatusRequest(
954 RequestSender* sender,
955 const GURL& upload_url,
956 int64 content_length,
957 const UploadRangeCallback& callback)
958 : GetUploadStatusRequestBase(sender,
959 upload_url,
960 content_length),
961 callback_(callback) {
962 DCHECK(!callback.is_null());
965 GetUploadStatusRequest::~GetUploadStatusRequest() {}
967 void GetUploadStatusRequest::OnRangeRequestComplete(
968 const UploadRangeResponse& response,
969 scoped_ptr<base::Value> value) {
970 DCHECK(CalledOnValidThread());
971 ParseFileResourceWithUploadRangeAndRun(callback_, response, value.Pass());
974 //======================= MultipartUploadNewFileRequest =======================
976 MultipartUploadNewFileRequest::MultipartUploadNewFileRequest(
977 RequestSender* sender,
978 const std::string& title,
979 const std::string& parent_resource_id,
980 const std::string& content_type,
981 int64 content_length,
982 const base::Time& modified_date,
983 const base::Time& last_viewed_by_me_date,
984 const base::FilePath& local_file_path,
985 const Properties& properties,
986 const DriveApiUrlGenerator& url_generator,
987 const FileResourceCallback& callback,
988 const ProgressCallback& progress_callback)
989 : MultipartUploadRequestBase(
990 sender,
991 CreateMultipartUploadMetadataJson(title,
992 parent_resource_id,
993 modified_date,
994 last_viewed_by_me_date,
995 properties),
996 content_type,
997 content_length,
998 local_file_path,
999 callback,
1000 progress_callback),
1001 has_modified_date_(!modified_date.is_null()),
1002 url_generator_(url_generator) {
1005 MultipartUploadNewFileRequest::~MultipartUploadNewFileRequest() {
1008 GURL MultipartUploadNewFileRequest::GetURL() const {
1009 return url_generator_.GetMultipartUploadNewFileUrl(has_modified_date_);
1012 net::URLFetcher::RequestType MultipartUploadNewFileRequest::GetRequestType()
1013 const {
1014 return net::URLFetcher::POST;
1017 //======================= MultipartUploadExistingFileRequest ===================
1019 MultipartUploadExistingFileRequest::MultipartUploadExistingFileRequest(
1020 RequestSender* sender,
1021 const std::string& title,
1022 const std::string& resource_id,
1023 const std::string& parent_resource_id,
1024 const std::string& content_type,
1025 int64 content_length,
1026 const base::Time& modified_date,
1027 const base::Time& last_viewed_by_me_date,
1028 const base::FilePath& local_file_path,
1029 const std::string& etag,
1030 const Properties& properties,
1031 const DriveApiUrlGenerator& url_generator,
1032 const FileResourceCallback& callback,
1033 const ProgressCallback& progress_callback)
1034 : MultipartUploadRequestBase(
1035 sender,
1036 CreateMultipartUploadMetadataJson(title,
1037 parent_resource_id,
1038 modified_date,
1039 last_viewed_by_me_date,
1040 properties),
1041 content_type,
1042 content_length,
1043 local_file_path,
1044 callback,
1045 progress_callback),
1046 resource_id_(resource_id),
1047 etag_(etag),
1048 has_modified_date_(!modified_date.is_null()),
1049 url_generator_(url_generator) {
1052 MultipartUploadExistingFileRequest::~MultipartUploadExistingFileRequest() {
1055 std::vector<std::string>
1056 MultipartUploadExistingFileRequest::GetExtraRequestHeaders() const {
1057 std::vector<std::string> headers(
1058 MultipartUploadRequestBase::GetExtraRequestHeaders());
1059 headers.push_back(util::GenerateIfMatchHeader(etag_));
1060 return headers;
1063 GURL MultipartUploadExistingFileRequest::GetURL() const {
1064 return url_generator_.GetMultipartUploadExistingFileUrl(resource_id_,
1065 has_modified_date_);
1068 net::URLFetcher::RequestType
1069 MultipartUploadExistingFileRequest::GetRequestType() const {
1070 return net::URLFetcher::PUT;
1073 //========================== DownloadFileRequest ==========================
1075 DownloadFileRequest::DownloadFileRequest(
1076 RequestSender* sender,
1077 const DriveApiUrlGenerator& url_generator,
1078 const std::string& resource_id,
1079 const base::FilePath& output_file_path,
1080 const DownloadActionCallback& download_action_callback,
1081 const GetContentCallback& get_content_callback,
1082 const ProgressCallback& progress_callback)
1083 : DownloadFileRequestBase(
1084 sender,
1085 download_action_callback,
1086 get_content_callback,
1087 progress_callback,
1088 url_generator.GenerateDownloadFileUrl(resource_id),
1089 output_file_path) {
1092 DownloadFileRequest::~DownloadFileRequest() {
1095 //========================== PermissionsInsertRequest ==========================
1097 PermissionsInsertRequest::PermissionsInsertRequest(
1098 RequestSender* sender,
1099 const DriveApiUrlGenerator& url_generator,
1100 const EntryActionCallback& callback)
1101 : EntryActionRequest(sender, callback),
1102 url_generator_(url_generator),
1103 type_(PERMISSION_TYPE_USER),
1104 role_(PERMISSION_ROLE_READER) {
1107 PermissionsInsertRequest::~PermissionsInsertRequest() {
1110 GURL PermissionsInsertRequest::GetURL() const {
1111 return url_generator_.GetPermissionsInsertUrl(id_);
1114 net::URLFetcher::RequestType
1115 PermissionsInsertRequest::GetRequestType() const {
1116 return net::URLFetcher::POST;
1119 bool PermissionsInsertRequest::GetContentData(std::string* upload_content_type,
1120 std::string* upload_content) {
1121 *upload_content_type = util::kContentTypeApplicationJson;
1123 base::DictionaryValue root;
1124 switch (type_) {
1125 case PERMISSION_TYPE_ANYONE:
1126 root.SetString("type", "anyone");
1127 break;
1128 case PERMISSION_TYPE_DOMAIN:
1129 root.SetString("type", "domain");
1130 break;
1131 case PERMISSION_TYPE_GROUP:
1132 root.SetString("type", "group");
1133 break;
1134 case PERMISSION_TYPE_USER:
1135 root.SetString("type", "user");
1136 break;
1138 switch (role_) {
1139 case PERMISSION_ROLE_OWNER:
1140 root.SetString("role", "owner");
1141 break;
1142 case PERMISSION_ROLE_READER:
1143 root.SetString("role", "reader");
1144 break;
1145 case PERMISSION_ROLE_WRITER:
1146 root.SetString("role", "writer");
1147 break;
1148 case PERMISSION_ROLE_COMMENTER:
1149 root.SetString("role", "reader");
1151 base::ListValue* list = new base::ListValue;
1152 list->AppendString("commenter");
1153 root.Set("additionalRoles", list);
1155 break;
1157 root.SetString("value", value_);
1158 base::JSONWriter::Write(&root, upload_content);
1159 return true;
1162 //========================== BatchUploadRequest ==========================
1164 BatchUploadRequest::BatchUploadRequest(
1165 RequestSender* sender,
1166 const DriveApiUrlGenerator& url_generator)
1167 : UrlFetchRequestBase(sender),
1168 sender_(sender),
1169 url_generator_(url_generator),
1170 committed_(false),
1171 last_progress_value_(0),
1172 weak_ptr_factory_(this) {
1175 BatchUploadRequest::~BatchUploadRequest() {
1176 for (const auto& child : child_requests_) {
1177 // Request will be deleted in |RequestFinished| method.
1178 sender_->RequestFinished(child.request);
1182 void BatchUploadRequest::SetBoundaryForTesting(const std::string& boundary) {
1183 boundary_ = boundary;
1186 void BatchUploadRequest::AddRequest(BatchableRequestBase* request) {
1187 DCHECK(CalledOnValidThread());
1188 DCHECK(request);
1189 DCHECK(GetChildEntry(request) == child_requests_.end());
1190 DCHECK(!committed_);
1191 child_requests_.push_back(BatchUploadChildEntry(request));
1192 request->Prepare(base::Bind(&BatchUploadRequest::OnChildRequestPrepared,
1193 weak_ptr_factory_.GetWeakPtr(), request));
1196 void BatchUploadRequest::OnChildRequestPrepared(RequestID request_id,
1197 DriveApiErrorCode result) {
1198 DCHECK(CalledOnValidThread());
1199 auto const child = GetChildEntry(request_id);
1200 DCHECK(child != child_requests_.end());
1201 if (IsSuccessfulDriveApiErrorCode(result)) {
1202 child->prepared = true;
1203 } else {
1204 child->request->RunCallbackOnPrematureFailure(result);
1205 sender_->RequestFinished(child->request);
1206 child_requests_.erase(child);
1208 MayCompletePrepare();
1211 void BatchUploadRequest::Commit() {
1212 DCHECK(CalledOnValidThread());
1213 DCHECK(!committed_);
1214 CHECK(!child_requests_.empty());
1215 committed_ = true;
1216 MayCompletePrepare();
1219 void BatchUploadRequest::Prepare(const PrepareCallback& callback) {
1220 DCHECK(CalledOnValidThread());
1221 DCHECK(!callback.is_null());
1222 prepare_callback_ = callback;
1223 MayCompletePrepare();
1226 void BatchUploadRequest::Cancel() {
1227 for (auto& child : child_requests_) {
1228 // Request cancel should delete the request instance.
1229 child.request->Cancel();
1231 child_requests_.clear();
1232 UrlFetchRequestBase::Cancel();
1235 // Obtains corresponding child entry of |request_id|. Returns NULL if the
1236 // entry is not found.
1237 std::vector<BatchUploadChildEntry>::iterator BatchUploadRequest::GetChildEntry(
1238 RequestID request_id) {
1239 for (auto it = child_requests_.begin(); it != child_requests_.end(); ++it) {
1240 if (it->request == request_id)
1241 return it;
1243 return child_requests_.end();
1246 void BatchUploadRequest::MayCompletePrepare() {
1247 if (!committed_ || prepare_callback_.is_null())
1248 return;
1249 for (const auto& child : child_requests_) {
1250 if (!child.prepared)
1251 return;
1254 // Build multipart body here.
1255 std::vector<ContentTypeAndData> parts;
1256 for (auto& child : child_requests_) {
1257 std::string type;
1258 std::string data;
1259 const bool result = child.request->GetContentData(&type, &data);
1260 // Upload request must have content data.
1261 DCHECK(result);
1263 const GURL url = child.request->GetURL();
1264 std::string method;
1265 switch (child.request->GetRequestType()) {
1266 case net::URLFetcher::POST:
1267 method = "POST";
1268 break;
1269 case net::URLFetcher::PUT:
1270 method = "PUT";
1271 break;
1272 default:
1273 NOTREACHED();
1274 break;
1276 const std::string header = base::StringPrintf(
1277 kBatchUploadRequestFormat, method.c_str(), url.path().c_str(),
1278 url_generator_.GetBatchUploadUrl().host().c_str(), type.c_str());
1280 child.data_offset = header.size();
1281 child.data_size = data.size();
1283 parts.push_back(ContentTypeAndData());
1284 parts.back().type = kHttpContentType;
1285 parts.back().data = header;
1286 parts.back().data.append(data);
1289 std::vector<uint64> part_data_offset;
1290 GenerateMultipartBody(MULTIPART_MIXED, boundary_, parts, &upload_content_,
1291 &part_data_offset);
1292 DCHECK(part_data_offset.size() == child_requests_.size());
1293 for (size_t i = 0; i < child_requests_.size(); ++i) {
1294 child_requests_[i].data_offset += part_data_offset[i];
1296 prepare_callback_.Run(HTTP_SUCCESS);
1299 bool BatchUploadRequest::GetContentData(std::string* upload_content_type,
1300 std::string* upload_content_data) {
1301 upload_content_type->assign(upload_content_.type);
1302 upload_content_data->assign(upload_content_.data);
1303 return true;
1306 base::WeakPtr<BatchUploadRequest>
1307 BatchUploadRequest::GetWeakPtrAsBatchUploadRequest() {
1308 return weak_ptr_factory_.GetWeakPtr();
1311 GURL BatchUploadRequest::GetURL() const {
1312 return url_generator_.GetBatchUploadUrl();
1315 net::URLFetcher::RequestType BatchUploadRequest::GetRequestType() const {
1316 return net::URLFetcher::PUT;
1319 std::vector<std::string> BatchUploadRequest::GetExtraRequestHeaders() const {
1320 std::vector<std::string> headers;
1321 headers.push_back(kBatchUploadHeader);
1322 return headers;
1325 void BatchUploadRequest::ProcessURLFetchResults(const net::URLFetcher* source) {
1326 if (!IsSuccessfulDriveApiErrorCode(GetErrorCode())) {
1327 RunCallbackOnPrematureFailure(GetErrorCode());
1328 sender_->RequestFinished(this);
1329 return;
1332 std::string content_type;
1333 source->GetResponseHeaders()->EnumerateHeader(
1334 /* need only first header */ NULL, "Content-Type", &content_type);
1336 std::vector<MultipartHttpResponse> parts;
1337 if (!ParseMultipartResponse(content_type, response_writer()->data(),
1338 &parts) ||
1339 child_requests_.size() != parts.size()) {
1340 RunCallbackOnPrematureFailure(DRIVE_PARSE_ERROR);
1341 sender_->RequestFinished(this);
1342 return;
1345 for (size_t i = 0; i < parts.size(); ++i) {
1346 child_requests_[i].request->ProcessURLFetchResults(parts[i].code,
1347 parts[i].body);
1350 child_requests_.clear();
1351 sender_->RequestFinished(this);
1354 void BatchUploadRequest::RunCallbackOnPrematureFailure(DriveApiErrorCode code) {
1355 for (auto child : child_requests_) {
1356 child.request->RunCallbackOnPrematureFailure(code);
1357 sender_->RequestFinished(child.request);
1359 child_requests_.clear();
1362 void BatchUploadRequest::OnURLFetchUploadProgress(const net::URLFetcher* source,
1363 int64 current,
1364 int64 total) {
1365 for (auto child : child_requests_) {
1366 if (child.data_offset <= current &&
1367 current <= child.data_offset + child.data_size) {
1368 child.request->OnURLFetchUploadProgress(
1369 source, current - child.data_offset, child.data_size);
1370 } else if (last_progress_value_ < child.data_offset + child.data_size &&
1371 child.data_offset + child.data_size < current) {
1372 child.request->OnURLFetchUploadProgress(source, child.data_size,
1373 child.data_size);
1376 last_progress_value_ = current;
1378 } // namespace drive
1379 } // namespace google_apis