| OLD | NEW |
| (Empty) |
| 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/chromeos/gdata/gdata_operations.h" | |
| 6 | |
| 7 #include "base/string_number_conversions.h" | |
| 8 #include "base/stringprintf.h" | |
| 9 #include "base/values.h" | |
| 10 #include "chrome/browser/chromeos/gdata/gdata_util.h" | |
| 11 #include "chrome/browser/chromeos/gdata/gdata_wapi_parser.h" | |
| 12 #include "chrome/common/net/url_util.h" | |
| 13 #include "content/public/browser/browser_thread.h" | |
| 14 #include "net/base/escape.h" | |
| 15 #include "net/http/http_util.h" | |
| 16 #include "third_party/libxml/chromium/libxml_utils.h" | |
| 17 | |
| 18 using net::URLFetcher; | |
| 19 | |
| 20 namespace { | |
| 21 | |
| 22 // etag matching header. | |
| 23 const char kIfMatchAllHeader[] = "If-Match: *"; | |
| 24 const char kIfMatchHeaderFormat[] = "If-Match: %s"; | |
| 25 | |
| 26 // URL requesting documents list that belong to the authenticated user only | |
| 27 // (handled with '/-/mine' part). | |
| 28 const char kGetDocumentListURLForAllDocuments[] = | |
| 29 "https://docs.google.com/feeds/default/private/full/-/mine"; | |
| 30 | |
| 31 // URL requesting documents list in a particular directory specified by "%s" | |
| 32 // that belong to the authenticated user only (handled with '/-/mine' part). | |
| 33 const char kGetDocumentListURLForDirectoryFormat[] = | |
| 34 "https://docs.google.com/feeds/default/private/full/%s/contents/-/mine"; | |
| 35 | |
| 36 // URL requesting documents list of changes to documents collections. | |
| 37 const char kGetChangesListURL[] = | |
| 38 "https://docs.google.com/feeds/default/private/changes"; | |
| 39 | |
| 40 // Root document list url. | |
| 41 const char kDocumentListRootURL[] = | |
| 42 "https://docs.google.com/feeds/default/private/full"; | |
| 43 | |
| 44 // URL requesting single document entry whose resource id is specified by "%s". | |
| 45 const char kGetDocumentEntryURLFormat[] = | |
| 46 "https://docs.google.com/feeds/default/private/full/%s"; | |
| 47 | |
| 48 // Metadata feed with things like user quota. | |
| 49 const char kAccountMetadataURL[] = | |
| 50 "https://docs.google.com/feeds/metadata/default"; | |
| 51 | |
| 52 // URL requesting all contact groups. | |
| 53 const char kGetContactGroupsURL[] = | |
| 54 "https://www.google.com/m8/feeds/groups/default/full?alt=json"; | |
| 55 | |
| 56 // URL requesting all contacts. | |
| 57 // TODO(derat): Per https://goo.gl/AufHP, "The feed may not contain all of the | |
| 58 // user's contacts, because there's a default limit on the number of results | |
| 59 // returned." Decide if 10000 is reasonable or not. | |
| 60 const char kGetContactsURL[] = | |
| 61 "https://www.google.com/m8/feeds/contacts/default/full" | |
| 62 "?alt=json&showdeleted=true&max-results=10000"; | |
| 63 | |
| 64 // Query parameter optionally appended to |kGetContactsURL| to return contacts | |
| 65 // from a specific group (as opposed to all contacts). | |
| 66 const char kGetContactsGroupParam[] = "group"; | |
| 67 | |
| 68 // Query parameter optionally appended to |kGetContactsURL| to return only | |
| 69 // recently-updated contacts. | |
| 70 const char kGetContactsUpdatedMinParam[] = "updated-min"; | |
| 71 | |
| 72 const char kUploadContentRange[] = "Content-Range: bytes "; | |
| 73 const char kUploadContentType[] = "X-Upload-Content-Type: "; | |
| 74 const char kUploadContentLength[] = "X-Upload-Content-Length: "; | |
| 75 | |
| 76 #ifndef NDEBUG | |
| 77 // Use smaller 'page' size while debugging to ensure we hit feed reload | |
| 78 // almost always. Be careful not to use something too small on account that | |
| 79 // have many items because server side 503 error might kick in. | |
| 80 const int kMaxDocumentsPerFeed = 500; | |
| 81 const int kMaxDocumentsPerSearchFeed = 50; | |
| 82 #else | |
| 83 const int kMaxDocumentsPerFeed = 500; | |
| 84 const int kMaxDocumentsPerSearchFeed = 50; | |
| 85 #endif | |
| 86 | |
| 87 const char kFeedField[] = "feed"; | |
| 88 | |
| 89 // Templates for file uploading. | |
| 90 const char kUploadParamConvertKey[] = "convert"; | |
| 91 const char kUploadParamConvertValue[] = "false"; | |
| 92 const char kUploadResponseLocation[] = "location"; | |
| 93 const char kUploadResponseRange[] = "range"; | |
| 94 | |
| 95 // Adds additional parameters for API version, output content type and to show | |
| 96 // folders in the feed are added to document feed URLs. | |
| 97 GURL AddStandardUrlParams(const GURL& url) { | |
| 98 GURL result = | |
| 99 chrome_common_net::AppendOrReplaceQueryParameter(url, "v", "3"); | |
| 100 result = | |
| 101 chrome_common_net::AppendOrReplaceQueryParameter(result, "alt", "json"); | |
| 102 return result; | |
| 103 } | |
| 104 | |
| 105 // Adds additional parameters to metadata feed to include installed 3rd party | |
| 106 // applications. | |
| 107 GURL AddMetadataUrlParams(const GURL& url) { | |
| 108 GURL result = AddStandardUrlParams(url); | |
| 109 result = chrome_common_net::AppendOrReplaceQueryParameter( | |
| 110 result, "include-installed-apps", "true"); | |
| 111 return result; | |
| 112 } | |
| 113 | |
| 114 // Adds additional parameters for API version, output content type and to show | |
| 115 // folders in the feed are added to document feed URLs. | |
| 116 GURL AddFeedUrlParams(const GURL& url, | |
| 117 int num_items_to_fetch, | |
| 118 int changestamp, | |
| 119 const std::string& search_string) { | |
| 120 GURL result = AddStandardUrlParams(url); | |
| 121 result = chrome_common_net::AppendOrReplaceQueryParameter( | |
| 122 result, | |
| 123 "showfolders", | |
| 124 "true"); | |
| 125 result = chrome_common_net::AppendOrReplaceQueryParameter( | |
| 126 result, | |
| 127 "max-results", | |
| 128 base::StringPrintf("%d", num_items_to_fetch)); | |
| 129 result = chrome_common_net::AppendOrReplaceQueryParameter( | |
| 130 result, "include-installed-apps", "true"); | |
| 131 | |
| 132 if (changestamp) { | |
| 133 result = chrome_common_net::AppendQueryParameter( | |
| 134 result, | |
| 135 "start-index", | |
| 136 base::StringPrintf("%d", changestamp)); | |
| 137 } | |
| 138 | |
| 139 if (!search_string.empty()) { | |
| 140 result = chrome_common_net::AppendOrReplaceQueryParameter( | |
| 141 result, "q", search_string); | |
| 142 } | |
| 143 return result; | |
| 144 } | |
| 145 | |
| 146 // Formats a URL for getting document list. If |directory_resource_id| is | |
| 147 // empty, returns a URL for fetching all documents. If it's given, returns a | |
| 148 // URL for fetching documents in a particular directory. | |
| 149 GURL FormatDocumentListURL(const std::string& directory_resource_id) { | |
| 150 if (directory_resource_id.empty()) | |
| 151 return GURL(kGetDocumentListURLForAllDocuments); | |
| 152 | |
| 153 return GURL(base::StringPrintf(kGetDocumentListURLForDirectoryFormat, | |
| 154 net::EscapePath( | |
| 155 directory_resource_id).c_str())); | |
| 156 } | |
| 157 | |
| 158 } // namespace | |
| 159 | |
| 160 namespace gdata { | |
| 161 | |
| 162 //============================ Structs =========================== | |
| 163 | |
| 164 ResumeUploadResponse::ResumeUploadResponse(GDataErrorCode code, | |
| 165 int64 start_range_received, | |
| 166 int64 end_range_received) | |
| 167 : code(code), | |
| 168 start_range_received(start_range_received), | |
| 169 end_range_received(end_range_received) { | |
| 170 } | |
| 171 | |
| 172 ResumeUploadResponse::~ResumeUploadResponse() { | |
| 173 } | |
| 174 | |
| 175 InitiateUploadParams::InitiateUploadParams( | |
| 176 UploadMode upload_mode, | |
| 177 const std::string& title, | |
| 178 const std::string& content_type, | |
| 179 int64 content_length, | |
| 180 const GURL& upload_location, | |
| 181 const FilePath& virtual_path) | |
| 182 : upload_mode(upload_mode), | |
| 183 title(title), | |
| 184 content_type(content_type), | |
| 185 content_length(content_length), | |
| 186 upload_location(upload_location), | |
| 187 virtual_path(virtual_path) { | |
| 188 } | |
| 189 | |
| 190 InitiateUploadParams::~InitiateUploadParams() { | |
| 191 } | |
| 192 | |
| 193 ResumeUploadParams::ResumeUploadParams( | |
| 194 UploadMode upload_mode, | |
| 195 int64 start_range, | |
| 196 int64 end_range, | |
| 197 int64 content_length, | |
| 198 const std::string& content_type, | |
| 199 scoped_refptr<net::IOBuffer> buf, | |
| 200 const GURL& upload_location, | |
| 201 const FilePath& virtual_path) : upload_mode(upload_mode), | |
| 202 start_range(start_range), | |
| 203 end_range(end_range), | |
| 204 content_length(content_length), | |
| 205 content_type(content_type), | |
| 206 buf(buf), | |
| 207 upload_location(upload_location), | |
| 208 virtual_path(virtual_path) { | |
| 209 } | |
| 210 | |
| 211 ResumeUploadParams::~ResumeUploadParams() { | |
| 212 } | |
| 213 | |
| 214 //============================ GetDocumentsOperation =========================== | |
| 215 | |
| 216 GetDocumentsOperation::GetDocumentsOperation( | |
| 217 OperationRegistry* registry, | |
| 218 const GURL& url, | |
| 219 int start_changestamp, | |
| 220 const std::string& search_string, | |
| 221 const std::string& directory_resource_id, | |
| 222 const GetDataCallback& callback) | |
| 223 : GetDataOperation(registry, callback), | |
| 224 override_url_(url), | |
| 225 start_changestamp_(start_changestamp), | |
| 226 search_string_(search_string), | |
| 227 directory_resource_id_(directory_resource_id) { | |
| 228 } | |
| 229 | |
| 230 GetDocumentsOperation::~GetDocumentsOperation() {} | |
| 231 | |
| 232 GURL GetDocumentsOperation::GetURL() const { | |
| 233 int max_docs = search_string_.empty() ? kMaxDocumentsPerFeed : | |
| 234 kMaxDocumentsPerSearchFeed; | |
| 235 | |
| 236 if (!override_url_.is_empty()) | |
| 237 return AddFeedUrlParams(override_url_, | |
| 238 max_docs, | |
| 239 0, | |
| 240 search_string_); | |
| 241 | |
| 242 if (start_changestamp_ == 0) { | |
| 243 return AddFeedUrlParams(FormatDocumentListURL(directory_resource_id_), | |
| 244 max_docs, | |
| 245 0, | |
| 246 search_string_); | |
| 247 } | |
| 248 | |
| 249 return AddFeedUrlParams(GURL(kGetChangesListURL), | |
| 250 kMaxDocumentsPerFeed, | |
| 251 start_changestamp_, | |
| 252 std::string()); | |
| 253 } | |
| 254 | |
| 255 //============================ GetDocumentEntryOperation ======================= | |
| 256 | |
| 257 GetDocumentEntryOperation::GetDocumentEntryOperation( | |
| 258 OperationRegistry* registry, | |
| 259 const std::string& resource_id, | |
| 260 const GetDataCallback& callback) | |
| 261 : GetDataOperation(registry, callback), | |
| 262 resource_id_(resource_id) { | |
| 263 } | |
| 264 | |
| 265 GetDocumentEntryOperation::~GetDocumentEntryOperation() {} | |
| 266 | |
| 267 GURL GetDocumentEntryOperation::GetURL() const { | |
| 268 GURL result = GURL(base::StringPrintf(kGetDocumentEntryURLFormat, | |
| 269 net::EscapePath(resource_id_).c_str())); | |
| 270 return AddStandardUrlParams(result); | |
| 271 } | |
| 272 | |
| 273 //========================= GetAccountMetadataOperation ======================== | |
| 274 | |
| 275 GetAccountMetadataOperation::GetAccountMetadataOperation( | |
| 276 OperationRegistry* registry, | |
| 277 const GetDataCallback& callback) | |
| 278 : GetDataOperation(registry, callback) { | |
| 279 } | |
| 280 | |
| 281 GetAccountMetadataOperation::~GetAccountMetadataOperation() {} | |
| 282 | |
| 283 GURL GetAccountMetadataOperation::GetURL() const { | |
| 284 return AddMetadataUrlParams(GURL(kAccountMetadataURL)); | |
| 285 } | |
| 286 | |
| 287 //============================ DownloadFileOperation =========================== | |
| 288 | |
| 289 DownloadFileOperation::DownloadFileOperation( | |
| 290 OperationRegistry* registry, | |
| 291 const DownloadActionCallback& download_action_callback, | |
| 292 const GetContentCallback& get_content_callback, | |
| 293 const GURL& document_url, | |
| 294 const FilePath& virtual_path, | |
| 295 const FilePath& output_file_path) | |
| 296 : UrlFetchOperationBase(registry, | |
| 297 OperationRegistry::OPERATION_DOWNLOAD, | |
| 298 virtual_path), | |
| 299 download_action_callback_(download_action_callback), | |
| 300 get_content_callback_(get_content_callback), | |
| 301 document_url_(document_url) { | |
| 302 // Make sure we download the content into a temp file. | |
| 303 if (output_file_path.empty()) | |
| 304 save_temp_file_ = true; | |
| 305 else | |
| 306 output_file_path_ = output_file_path; | |
| 307 } | |
| 308 | |
| 309 DownloadFileOperation::~DownloadFileOperation() {} | |
| 310 | |
| 311 // Overridden from UrlFetchOperationBase. | |
| 312 GURL DownloadFileOperation::GetURL() const { | |
| 313 return document_url_; | |
| 314 } | |
| 315 | |
| 316 void DownloadFileOperation::OnURLFetchDownloadProgress(const URLFetcher* source, | |
| 317 int64 current, | |
| 318 int64 total) { | |
| 319 NotifyProgress(current, total); | |
| 320 } | |
| 321 | |
| 322 bool DownloadFileOperation::ShouldSendDownloadData() { | |
| 323 return !get_content_callback_.is_null(); | |
| 324 } | |
| 325 | |
| 326 void DownloadFileOperation::OnURLFetchDownloadData( | |
| 327 const URLFetcher* source, | |
| 328 scoped_ptr<std::string> download_data) { | |
| 329 if (!get_content_callback_.is_null()) | |
| 330 get_content_callback_.Run(HTTP_SUCCESS, download_data.Pass()); | |
| 331 } | |
| 332 | |
| 333 void DownloadFileOperation::ProcessURLFetchResults(const URLFetcher* source) { | |
| 334 GDataErrorCode code = GetErrorCode(source); | |
| 335 | |
| 336 // Take over the ownership of the the downloaded temp file. | |
| 337 FilePath temp_file; | |
| 338 if (code == HTTP_SUCCESS && | |
| 339 !source->GetResponseAsFilePath(true, // take_ownership | |
| 340 &temp_file)) { | |
| 341 code = GDATA_FILE_ERROR; | |
| 342 } | |
| 343 | |
| 344 if (!download_action_callback_.is_null()) | |
| 345 download_action_callback_.Run(code, document_url_, temp_file); | |
| 346 OnProcessURLFetchResultsComplete(code == HTTP_SUCCESS); | |
| 347 } | |
| 348 | |
| 349 void DownloadFileOperation::RunCallbackOnPrematureFailure(GDataErrorCode code) { | |
| 350 if (!download_action_callback_.is_null()) | |
| 351 download_action_callback_.Run(code, document_url_, FilePath()); | |
| 352 } | |
| 353 | |
| 354 //=========================== DeleteDocumentOperation ========================== | |
| 355 | |
| 356 DeleteDocumentOperation::DeleteDocumentOperation( | |
| 357 OperationRegistry* registry, | |
| 358 const EntryActionCallback& callback, | |
| 359 const GURL& document_url) | |
| 360 : EntryActionOperation(registry, callback, document_url) { | |
| 361 } | |
| 362 | |
| 363 DeleteDocumentOperation::~DeleteDocumentOperation() {} | |
| 364 | |
| 365 GURL DeleteDocumentOperation::GetURL() const { | |
| 366 return AddStandardUrlParams(document_url()); | |
| 367 } | |
| 368 | |
| 369 URLFetcher::RequestType DeleteDocumentOperation::GetRequestType() const { | |
| 370 return URLFetcher::DELETE_REQUEST; | |
| 371 } | |
| 372 | |
| 373 std::vector<std::string> | |
| 374 DeleteDocumentOperation::GetExtraRequestHeaders() const { | |
| 375 std::vector<std::string> headers; | |
| 376 headers.push_back(kIfMatchAllHeader); | |
| 377 return headers; | |
| 378 } | |
| 379 | |
| 380 //========================== CreateDirectoryOperation ========================== | |
| 381 | |
| 382 CreateDirectoryOperation::CreateDirectoryOperation( | |
| 383 OperationRegistry* registry, | |
| 384 const GetDataCallback& callback, | |
| 385 const GURL& parent_content_url, | |
| 386 const FilePath::StringType& directory_name) | |
| 387 : GetDataOperation(registry, callback), | |
| 388 parent_content_url_(parent_content_url), | |
| 389 directory_name_(directory_name) { | |
| 390 } | |
| 391 | |
| 392 CreateDirectoryOperation::~CreateDirectoryOperation() {} | |
| 393 | |
| 394 GURL CreateDirectoryOperation::GetURL() const { | |
| 395 if (!parent_content_url_.is_empty()) | |
| 396 return AddStandardUrlParams(parent_content_url_); | |
| 397 | |
| 398 return AddStandardUrlParams(GURL(kDocumentListRootURL)); | |
| 399 } | |
| 400 | |
| 401 URLFetcher::RequestType | |
| 402 CreateDirectoryOperation::GetRequestType() const { | |
| 403 return URLFetcher::POST; | |
| 404 } | |
| 405 | |
| 406 bool CreateDirectoryOperation::GetContentData(std::string* upload_content_type, | |
| 407 std::string* upload_content) { | |
| 408 upload_content_type->assign("application/atom+xml"); | |
| 409 XmlWriter xml_writer; | |
| 410 xml_writer.StartWriting(); | |
| 411 xml_writer.StartElement("entry"); | |
| 412 xml_writer.AddAttribute("xmlns", "http://www.w3.org/2005/Atom"); | |
| 413 | |
| 414 xml_writer.StartElement("category"); | |
| 415 xml_writer.AddAttribute("scheme", | |
| 416 "http://schemas.google.com/g/2005#kind"); | |
| 417 xml_writer.AddAttribute("term", | |
| 418 "http://schemas.google.com/docs/2007#folder"); | |
| 419 xml_writer.EndElement(); // Ends "category" element. | |
| 420 | |
| 421 xml_writer.WriteElement("title", FilePath(directory_name_).AsUTF8Unsafe()); | |
| 422 | |
| 423 xml_writer.EndElement(); // Ends "entry" element. | |
| 424 xml_writer.StopWriting(); | |
| 425 upload_content->assign(xml_writer.GetWrittenString()); | |
| 426 DVLOG(1) << "CreateDirectory data: " << *upload_content_type << ", [" | |
| 427 << *upload_content << "]"; | |
| 428 return true; | |
| 429 } | |
| 430 | |
| 431 //============================ CopyDocumentOperation =========================== | |
| 432 | |
| 433 CopyDocumentOperation::CopyDocumentOperation( | |
| 434 OperationRegistry* registry, | |
| 435 const GetDataCallback& callback, | |
| 436 const std::string& resource_id, | |
| 437 const FilePath::StringType& new_name) | |
| 438 : GetDataOperation(registry, callback), | |
| 439 resource_id_(resource_id), | |
| 440 new_name_(new_name) { | |
| 441 } | |
| 442 | |
| 443 CopyDocumentOperation::~CopyDocumentOperation() {} | |
| 444 | |
| 445 URLFetcher::RequestType CopyDocumentOperation::GetRequestType() const { | |
| 446 return URLFetcher::POST; | |
| 447 } | |
| 448 | |
| 449 GURL CopyDocumentOperation::GetURL() const { | |
| 450 return AddStandardUrlParams(GURL(kDocumentListRootURL)); | |
| 451 } | |
| 452 | |
| 453 bool CopyDocumentOperation::GetContentData(std::string* upload_content_type, | |
| 454 std::string* upload_content) { | |
| 455 upload_content_type->assign("application/atom+xml"); | |
| 456 XmlWriter xml_writer; | |
| 457 xml_writer.StartWriting(); | |
| 458 xml_writer.StartElement("entry"); | |
| 459 xml_writer.AddAttribute("xmlns", "http://www.w3.org/2005/Atom"); | |
| 460 | |
| 461 xml_writer.WriteElement("id", resource_id_); | |
| 462 xml_writer.WriteElement("title", FilePath(new_name_).AsUTF8Unsafe()); | |
| 463 | |
| 464 xml_writer.EndElement(); // Ends "entry" element. | |
| 465 xml_writer.StopWriting(); | |
| 466 upload_content->assign(xml_writer.GetWrittenString()); | |
| 467 DVLOG(1) << "CopyDocumentOperation data: " << *upload_content_type << ", [" | |
| 468 << *upload_content << "]"; | |
| 469 return true; | |
| 470 } | |
| 471 | |
| 472 //=========================== RenameResourceOperation ========================== | |
| 473 | |
| 474 RenameResourceOperation::RenameResourceOperation( | |
| 475 OperationRegistry* registry, | |
| 476 const EntryActionCallback& callback, | |
| 477 const GURL& document_url, | |
| 478 const FilePath::StringType& new_name) | |
| 479 : EntryActionOperation(registry, callback, document_url), | |
| 480 new_name_(new_name) { | |
| 481 } | |
| 482 | |
| 483 RenameResourceOperation::~RenameResourceOperation() {} | |
| 484 | |
| 485 URLFetcher::RequestType RenameResourceOperation::GetRequestType() const { | |
| 486 return URLFetcher::PUT; | |
| 487 } | |
| 488 | |
| 489 std::vector<std::string> | |
| 490 RenameResourceOperation::GetExtraRequestHeaders() const { | |
| 491 std::vector<std::string> headers; | |
| 492 headers.push_back(kIfMatchAllHeader); | |
| 493 return headers; | |
| 494 } | |
| 495 | |
| 496 GURL RenameResourceOperation::GetURL() const { | |
| 497 return AddStandardUrlParams(document_url()); | |
| 498 } | |
| 499 | |
| 500 bool RenameResourceOperation::GetContentData(std::string* upload_content_type, | |
| 501 std::string* upload_content) { | |
| 502 upload_content_type->assign("application/atom+xml"); | |
| 503 XmlWriter xml_writer; | |
| 504 xml_writer.StartWriting(); | |
| 505 xml_writer.StartElement("entry"); | |
| 506 xml_writer.AddAttribute("xmlns", "http://www.w3.org/2005/Atom"); | |
| 507 | |
| 508 xml_writer.WriteElement("title", FilePath(new_name_).AsUTF8Unsafe()); | |
| 509 | |
| 510 xml_writer.EndElement(); // Ends "entry" element. | |
| 511 xml_writer.StopWriting(); | |
| 512 upload_content->assign(xml_writer.GetWrittenString()); | |
| 513 DVLOG(1) << "RenameResourceOperation data: " << *upload_content_type << ", [" | |
| 514 << *upload_content << "]"; | |
| 515 return true; | |
| 516 } | |
| 517 | |
| 518 //=========================== AuthorizeAppOperation ========================== | |
| 519 | |
| 520 AuthorizeAppsOperation::AuthorizeAppsOperation( | |
| 521 OperationRegistry* registry, | |
| 522 const GetDataCallback& callback, | |
| 523 const GURL& document_url, | |
| 524 const std::string& app_id) | |
| 525 : GetDataOperation(registry, callback), | |
| 526 app_id_(app_id), | |
| 527 document_url_(document_url) { | |
| 528 } | |
| 529 | |
| 530 AuthorizeAppsOperation::~AuthorizeAppsOperation() {} | |
| 531 | |
| 532 URLFetcher::RequestType AuthorizeAppsOperation::GetRequestType() const { | |
| 533 return URLFetcher::PUT; | |
| 534 } | |
| 535 | |
| 536 std::vector<std::string> | |
| 537 AuthorizeAppsOperation::GetExtraRequestHeaders() const { | |
| 538 std::vector<std::string> headers; | |
| 539 headers.push_back(kIfMatchAllHeader); | |
| 540 return headers; | |
| 541 } | |
| 542 | |
| 543 void AuthorizeAppsOperation::ProcessURLFetchResults(const URLFetcher* source) { | |
| 544 std::string data; | |
| 545 source->GetResponseAsString(&data); | |
| 546 GetDataOperation::ProcessURLFetchResults(source); | |
| 547 } | |
| 548 | |
| 549 bool AuthorizeAppsOperation::GetContentData(std::string* upload_content_type, | |
| 550 std::string* upload_content) { | |
| 551 upload_content_type->assign("application/atom+xml"); | |
| 552 XmlWriter xml_writer; | |
| 553 xml_writer.StartWriting(); | |
| 554 xml_writer.StartElement("entry"); | |
| 555 xml_writer.AddAttribute("xmlns", "http://www.w3.org/2005/Atom"); | |
| 556 xml_writer.AddAttribute("xmlns:docs", "http://schemas.google.com/docs/2007"); | |
| 557 xml_writer.WriteElement("docs:authorizedApp", app_id_); | |
| 558 | |
| 559 xml_writer.EndElement(); // Ends "entry" element. | |
| 560 xml_writer.StopWriting(); | |
| 561 upload_content->assign(xml_writer.GetWrittenString()); | |
| 562 DVLOG(1) << "AuthorizeAppOperation data: " << *upload_content_type << ", [" | |
| 563 << *upload_content << "]"; | |
| 564 return true; | |
| 565 } | |
| 566 | |
| 567 void AuthorizeAppsOperation::ParseResponse( | |
| 568 GDataErrorCode fetch_error_code, | |
| 569 const std::string& data) { | |
| 570 // Parse entry XML. | |
| 571 XmlReader xml_reader; | |
| 572 scoped_ptr<DocumentEntry> entry; | |
| 573 if (xml_reader.Load(data)) { | |
| 574 while (xml_reader.Read()) { | |
| 575 if (xml_reader.NodeName() == DocumentEntry::GetEntryNodeName()) { | |
| 576 entry.reset(DocumentEntry::CreateFromXml(&xml_reader)); | |
| 577 break; | |
| 578 } | |
| 579 } | |
| 580 } | |
| 581 | |
| 582 // From the response, we create a list of the links returned, since those | |
| 583 // are the only things we are interested in. | |
| 584 scoped_ptr<base::ListValue> link_list(new ListValue); | |
| 585 const ScopedVector<Link>& feed_links = entry->links(); | |
| 586 for (ScopedVector<Link>::const_iterator iter = feed_links.begin(); | |
| 587 iter != feed_links.end(); ++iter) { | |
| 588 if ((*iter)->type() == Link::LINK_OPEN_WITH) { | |
| 589 base::DictionaryValue* link = new DictionaryValue; | |
| 590 link->SetString(std::string("href"), (*iter)->href().spec()); | |
| 591 link->SetString(std::string("mime_type"), (*iter)->mime_type()); | |
| 592 link->SetString(std::string("title"), (*iter)->title()); | |
| 593 link->SetString(std::string("app_id"), (*iter)->app_id()); | |
| 594 link_list->Append(link); | |
| 595 } | |
| 596 } | |
| 597 | |
| 598 RunCallback(fetch_error_code, link_list.PassAs<base::Value>()); | |
| 599 const bool success = true; | |
| 600 OnProcessURLFetchResultsComplete(success); | |
| 601 } | |
| 602 | |
| 603 GURL AuthorizeAppsOperation::GetURL() const { | |
| 604 return document_url_; | |
| 605 } | |
| 606 | |
| 607 //======================= AddResourceToDirectoryOperation ====================== | |
| 608 | |
| 609 AddResourceToDirectoryOperation::AddResourceToDirectoryOperation( | |
| 610 OperationRegistry* registry, | |
| 611 const EntryActionCallback& callback, | |
| 612 const GURL& parent_content_url, | |
| 613 const GURL& document_url) | |
| 614 : EntryActionOperation(registry, callback, document_url), | |
| 615 parent_content_url_(parent_content_url) { | |
| 616 } | |
| 617 | |
| 618 AddResourceToDirectoryOperation::~AddResourceToDirectoryOperation() {} | |
| 619 | |
| 620 GURL AddResourceToDirectoryOperation::GetURL() const { | |
| 621 if (!parent_content_url_.is_empty()) | |
| 622 return AddStandardUrlParams(parent_content_url_); | |
| 623 | |
| 624 return AddStandardUrlParams(GURL(kDocumentListRootURL)); | |
| 625 } | |
| 626 | |
| 627 URLFetcher::RequestType | |
| 628 AddResourceToDirectoryOperation::GetRequestType() const { | |
| 629 return URLFetcher::POST; | |
| 630 } | |
| 631 | |
| 632 bool AddResourceToDirectoryOperation::GetContentData( | |
| 633 std::string* upload_content_type, std::string* upload_content) { | |
| 634 upload_content_type->assign("application/atom+xml"); | |
| 635 XmlWriter xml_writer; | |
| 636 xml_writer.StartWriting(); | |
| 637 xml_writer.StartElement("entry"); | |
| 638 xml_writer.AddAttribute("xmlns", "http://www.w3.org/2005/Atom"); | |
| 639 | |
| 640 xml_writer.WriteElement("id", document_url().spec()); | |
| 641 | |
| 642 xml_writer.EndElement(); // Ends "entry" element. | |
| 643 xml_writer.StopWriting(); | |
| 644 upload_content->assign(xml_writer.GetWrittenString()); | |
| 645 DVLOG(1) << "AddResourceToDirectoryOperation data: " << *upload_content_type | |
| 646 << ", [" << *upload_content << "]"; | |
| 647 return true; | |
| 648 } | |
| 649 | |
| 650 //==================== RemoveResourceFromDirectoryOperation ==================== | |
| 651 | |
| 652 RemoveResourceFromDirectoryOperation::RemoveResourceFromDirectoryOperation( | |
| 653 OperationRegistry* registry, | |
| 654 const EntryActionCallback& callback, | |
| 655 const GURL& parent_content_url, | |
| 656 const GURL& document_url, | |
| 657 const std::string& document_resource_id) | |
| 658 : EntryActionOperation(registry, callback, document_url), | |
| 659 resource_id_(document_resource_id), | |
| 660 parent_content_url_(parent_content_url) { | |
| 661 } | |
| 662 | |
| 663 RemoveResourceFromDirectoryOperation::~RemoveResourceFromDirectoryOperation() { | |
| 664 } | |
| 665 | |
| 666 GURL RemoveResourceFromDirectoryOperation::GetURL() const { | |
| 667 std::string escaped_resource_id = net::EscapePath(resource_id_); | |
| 668 GURL edit_url(base::StringPrintf("%s/%s", | |
| 669 parent_content_url_.spec().c_str(), | |
| 670 escaped_resource_id.c_str())); | |
| 671 return AddStandardUrlParams(edit_url); | |
| 672 } | |
| 673 | |
| 674 URLFetcher::RequestType | |
| 675 RemoveResourceFromDirectoryOperation::GetRequestType() const { | |
| 676 return URLFetcher::DELETE_REQUEST; | |
| 677 } | |
| 678 | |
| 679 std::vector<std::string> | |
| 680 RemoveResourceFromDirectoryOperation::GetExtraRequestHeaders() const { | |
| 681 std::vector<std::string> headers; | |
| 682 headers.push_back(kIfMatchAllHeader); | |
| 683 return headers; | |
| 684 } | |
| 685 | |
| 686 //=========================== InitiateUploadOperation ========================== | |
| 687 | |
| 688 InitiateUploadOperation::InitiateUploadOperation( | |
| 689 OperationRegistry* registry, | |
| 690 const InitiateUploadCallback& callback, | |
| 691 const InitiateUploadParams& params) | |
| 692 : UrlFetchOperationBase(registry, | |
| 693 OperationRegistry::OPERATION_UPLOAD, | |
| 694 params.virtual_path), | |
| 695 callback_(callback), | |
| 696 params_(params), | |
| 697 initiate_upload_url_(chrome_common_net::AppendOrReplaceQueryParameter( | |
| 698 params.upload_location, | |
| 699 kUploadParamConvertKey, | |
| 700 kUploadParamConvertValue)) { | |
| 701 } | |
| 702 | |
| 703 InitiateUploadOperation::~InitiateUploadOperation() {} | |
| 704 | |
| 705 GURL InitiateUploadOperation::GetURL() const { | |
| 706 return initiate_upload_url_; | |
| 707 } | |
| 708 | |
| 709 void InitiateUploadOperation::ProcessURLFetchResults( | |
| 710 const URLFetcher* source) { | |
| 711 GDataErrorCode code = GetErrorCode(source); | |
| 712 | |
| 713 std::string upload_location; | |
| 714 if (code == HTTP_SUCCESS) { | |
| 715 // Retrieve value of the first "Location" header. | |
| 716 source->GetResponseHeaders()->EnumerateHeader(NULL, | |
| 717 kUploadResponseLocation, | |
| 718 &upload_location); | |
| 719 } | |
| 720 VLOG(1) << "Got response for [" << params_.title | |
| 721 << "]: code=" << code | |
| 722 << ", location=[" << upload_location << "]"; | |
| 723 | |
| 724 if (!callback_.is_null()) | |
| 725 callback_.Run(code, GURL(upload_location)); | |
| 726 OnProcessURLFetchResultsComplete(code == HTTP_SUCCESS); | |
| 727 } | |
| 728 | |
| 729 void InitiateUploadOperation::NotifySuccessToOperationRegistry() { | |
| 730 NotifySuspend(); | |
| 731 } | |
| 732 | |
| 733 void InitiateUploadOperation::RunCallbackOnPrematureFailure( | |
| 734 GDataErrorCode code) { | |
| 735 if (!callback_.is_null()) | |
| 736 callback_.Run(code, GURL()); | |
| 737 } | |
| 738 | |
| 739 URLFetcher::RequestType InitiateUploadOperation::GetRequestType() const { | |
| 740 if (params_.upload_mode == UPLOAD_NEW_FILE) | |
| 741 return URLFetcher::POST; | |
| 742 | |
| 743 DCHECK_EQ(UPLOAD_EXISTING_FILE, params_.upload_mode); | |
| 744 return URLFetcher::PUT; | |
| 745 } | |
| 746 | |
| 747 std::vector<std::string> | |
| 748 InitiateUploadOperation::GetExtraRequestHeaders() const { | |
| 749 std::vector<std::string> headers; | |
| 750 if (!params_.content_type.empty()) | |
| 751 headers.push_back(kUploadContentType + params_.content_type); | |
| 752 | |
| 753 headers.push_back( | |
| 754 kUploadContentLength + base::Int64ToString(params_.content_length)); | |
| 755 | |
| 756 if (params_.upload_mode == UPLOAD_EXISTING_FILE) | |
| 757 headers.push_back("If-Match: *"); | |
| 758 | |
| 759 return headers; | |
| 760 } | |
| 761 | |
| 762 bool InitiateUploadOperation::GetContentData(std::string* upload_content_type, | |
| 763 std::string* upload_content) { | |
| 764 if (params_.upload_mode == UPLOAD_EXISTING_FILE) { | |
| 765 // When uploading an existing file, the body is empty as we don't modify | |
| 766 // the metadata. | |
| 767 *upload_content = ""; | |
| 768 // Even though the body is empty, Content-Type should be set to | |
| 769 // "text/plain". Otherwise, the server won't accept. | |
| 770 *upload_content_type = "text/plain"; | |
| 771 return true; | |
| 772 } | |
| 773 | |
| 774 DCHECK_EQ(UPLOAD_NEW_FILE, params_.upload_mode); | |
| 775 upload_content_type->assign("application/atom+xml"); | |
| 776 XmlWriter xml_writer; | |
| 777 xml_writer.StartWriting(); | |
| 778 xml_writer.StartElement("entry"); | |
| 779 xml_writer.AddAttribute("xmlns", "http://www.w3.org/2005/Atom"); | |
| 780 xml_writer.AddAttribute("xmlns:docs", | |
| 781 "http://schemas.google.com/docs/2007"); | |
| 782 xml_writer.WriteElement("title", params_.title); | |
| 783 xml_writer.EndElement(); // Ends "entry" element. | |
| 784 xml_writer.StopWriting(); | |
| 785 upload_content->assign(xml_writer.GetWrittenString()); | |
| 786 DVLOG(1) << "Upload data: " << *upload_content_type << ", [" | |
| 787 << *upload_content << "]"; | |
| 788 return true; | |
| 789 } | |
| 790 | |
| 791 //============================ ResumeUploadOperation =========================== | |
| 792 | |
| 793 ResumeUploadOperation::ResumeUploadOperation( | |
| 794 OperationRegistry* registry, | |
| 795 const ResumeUploadCallback& callback, | |
| 796 const ResumeUploadParams& params) | |
| 797 : UrlFetchOperationBase(registry, | |
| 798 OperationRegistry::OPERATION_UPLOAD, | |
| 799 params.virtual_path), | |
| 800 callback_(callback), | |
| 801 params_(params), | |
| 802 last_chunk_completed_(false) { | |
| 803 } | |
| 804 | |
| 805 ResumeUploadOperation::~ResumeUploadOperation() {} | |
| 806 | |
| 807 GURL ResumeUploadOperation::GetURL() const { | |
| 808 return params_.upload_location; | |
| 809 } | |
| 810 | |
| 811 void ResumeUploadOperation::ProcessURLFetchResults(const URLFetcher* source) { | |
| 812 GDataErrorCode code = GetErrorCode(source); | |
| 813 net::HttpResponseHeaders* hdrs = source->GetResponseHeaders(); | |
| 814 int64 start_range_received = -1; | |
| 815 int64 end_range_received = -1; | |
| 816 scoped_ptr<DocumentEntry> entry; | |
| 817 | |
| 818 if (code == HTTP_RESUME_INCOMPLETE) { | |
| 819 // Retrieve value of the first "Range" header. | |
| 820 std::string range_received; | |
| 821 hdrs->EnumerateHeader(NULL, kUploadResponseRange, &range_received); | |
| 822 if (!range_received.empty()) { // Parse the range header. | |
| 823 std::vector<net::HttpByteRange> ranges; | |
| 824 if (net::HttpUtil::ParseRangeHeader(range_received, &ranges) && | |
| 825 !ranges.empty() ) { | |
| 826 // We only care about the first start-end pair in the range. | |
| 827 start_range_received = ranges[0].first_byte_position(); | |
| 828 end_range_received = ranges[0].last_byte_position(); | |
| 829 } | |
| 830 } | |
| 831 DVLOG(1) << "Got response for [" << params_.virtual_path.value() | |
| 832 << "]: code=" << code | |
| 833 << ", range_hdr=[" << range_received | |
| 834 << "], range_parsed=" << start_range_received | |
| 835 << "," << end_range_received; | |
| 836 } else { | |
| 837 // There might be explanation of unexpected error code in response. | |
| 838 std::string response_content; | |
| 839 source->GetResponseAsString(&response_content); | |
| 840 DVLOG(1) << "Got response for [" << params_.virtual_path.value() | |
| 841 << "]: code=" << code | |
| 842 << ", content=[\n" << response_content << "\n]"; | |
| 843 | |
| 844 // Parse entry XML. | |
| 845 XmlReader xml_reader; | |
| 846 if (xml_reader.Load(response_content)) { | |
| 847 while (xml_reader.Read()) { | |
| 848 if (xml_reader.NodeName() == DocumentEntry::GetEntryNodeName()) { | |
| 849 entry.reset(DocumentEntry::CreateFromXml(&xml_reader)); | |
| 850 break; | |
| 851 } | |
| 852 } | |
| 853 } | |
| 854 if (!entry.get()) | |
| 855 LOG(WARNING) << "Invalid entry received on upload:\n" << response_content; | |
| 856 } | |
| 857 | |
| 858 if (!callback_.is_null()) { | |
| 859 callback_.Run(ResumeUploadResponse(code, | |
| 860 start_range_received, | |
| 861 end_range_received), | |
| 862 entry.Pass()); | |
| 863 } | |
| 864 | |
| 865 // For a new file, HTTP_CREATED is returned. | |
| 866 // For an existing file, HTTP_SUCCESS is returned. | |
| 867 if ((params_.upload_mode == UPLOAD_NEW_FILE && code == HTTP_CREATED) || | |
| 868 (params_.upload_mode == UPLOAD_EXISTING_FILE && code == HTTP_SUCCESS)) { | |
| 869 last_chunk_completed_ = true; | |
| 870 } | |
| 871 | |
| 872 OnProcessURLFetchResultsComplete( | |
| 873 last_chunk_completed_ || code == HTTP_RESUME_INCOMPLETE); | |
| 874 } | |
| 875 | |
| 876 void ResumeUploadOperation::NotifyStartToOperationRegistry() { | |
| 877 NotifyResume(); | |
| 878 } | |
| 879 | |
| 880 void ResumeUploadOperation::NotifySuccessToOperationRegistry() { | |
| 881 if (last_chunk_completed_) | |
| 882 NotifyFinish(OperationRegistry::OPERATION_COMPLETED); | |
| 883 else | |
| 884 NotifySuspend(); | |
| 885 } | |
| 886 | |
| 887 void ResumeUploadOperation::RunCallbackOnPrematureFailure(GDataErrorCode code) { | |
| 888 scoped_ptr<DocumentEntry> entry; | |
| 889 if (!callback_.is_null()) | |
| 890 callback_.Run(ResumeUploadResponse(code, 0, 0), entry.Pass()); | |
| 891 } | |
| 892 | |
| 893 URLFetcher::RequestType ResumeUploadOperation::GetRequestType() const { | |
| 894 return URLFetcher::PUT; | |
| 895 } | |
| 896 | |
| 897 std::vector<std::string> ResumeUploadOperation::GetExtraRequestHeaders() const { | |
| 898 if (params_.content_length == 0) { | |
| 899 // For uploading an empty document, just PUT an empty content. | |
| 900 DCHECK_EQ(params_.start_range, 0); | |
| 901 DCHECK_EQ(params_.end_range, -1); | |
| 902 return std::vector<std::string>(); | |
| 903 } | |
| 904 | |
| 905 // The header looks like | |
| 906 // Content-Range: bytes <start_range>-<end_range>/<content_length> | |
| 907 // for example: | |
| 908 // Content-Range: bytes 7864320-8388607/13851821 | |
| 909 // Use * for unknown/streaming content length. | |
| 910 DCHECK_GE(params_.start_range, 0); | |
| 911 DCHECK_GE(params_.end_range, 0); | |
| 912 DCHECK_GE(params_.content_length, -1); | |
| 913 | |
| 914 std::vector<std::string> headers; | |
| 915 headers.push_back( | |
| 916 std::string(kUploadContentRange) + | |
| 917 base::Int64ToString(params_.start_range) + "-" + | |
| 918 base::Int64ToString(params_.end_range) + "/" + | |
| 919 (params_.content_length == -1 ? "*" : | |
| 920 base::Int64ToString(params_.content_length))); | |
| 921 return headers; | |
| 922 } | |
| 923 | |
| 924 bool ResumeUploadOperation::GetContentData(std::string* upload_content_type, | |
| 925 std::string* upload_content) { | |
| 926 *upload_content_type = params_.content_type; | |
| 927 *upload_content = std::string(params_.buf->data(), | |
| 928 params_.end_range - params_.start_range + 1); | |
| 929 return true; | |
| 930 } | |
| 931 | |
| 932 void ResumeUploadOperation::OnURLFetchUploadProgress( | |
| 933 const URLFetcher* source, int64 current, int64 total) { | |
| 934 // Adjust the progress values according to the range currently uploaded. | |
| 935 NotifyProgress(params_.start_range + current, params_.content_length); | |
| 936 } | |
| 937 | |
| 938 //========================== GetContactGroupsOperation ========================= | |
| 939 | |
| 940 GetContactGroupsOperation::GetContactGroupsOperation( | |
| 941 OperationRegistry* registry, | |
| 942 const GetDataCallback& callback) | |
| 943 : GetDataOperation(registry, callback) { | |
| 944 } | |
| 945 | |
| 946 GetContactGroupsOperation::~GetContactGroupsOperation() {} | |
| 947 | |
| 948 GURL GetContactGroupsOperation::GetURL() const { | |
| 949 return !feed_url_for_testing_.is_empty() ? | |
| 950 feed_url_for_testing_ : | |
| 951 GURL(kGetContactGroupsURL); | |
| 952 } | |
| 953 | |
| 954 //============================ GetContactsOperation ============================ | |
| 955 | |
| 956 GetContactsOperation::GetContactsOperation(OperationRegistry* registry, | |
| 957 const std::string& group_id, | |
| 958 const base::Time& min_update_time, | |
| 959 const GetDataCallback& callback) | |
| 960 : GetDataOperation(registry, callback), | |
| 961 group_id_(group_id), | |
| 962 min_update_time_(min_update_time) { | |
| 963 } | |
| 964 | |
| 965 GetContactsOperation::~GetContactsOperation() {} | |
| 966 | |
| 967 GURL GetContactsOperation::GetURL() const { | |
| 968 if (!feed_url_for_testing_.is_empty()) | |
| 969 return GURL(feed_url_for_testing_); | |
| 970 | |
| 971 GURL url(kGetContactsURL); | |
| 972 | |
| 973 if (!group_id_.empty()) { | |
| 974 url = chrome_common_net::AppendQueryParameter( | |
| 975 url, kGetContactsGroupParam, group_id_); | |
| 976 } | |
| 977 if (!min_update_time_.is_null()) { | |
| 978 std::string time_rfc3339 = util::FormatTimeAsString(min_update_time_); | |
| 979 url = chrome_common_net::AppendQueryParameter( | |
| 980 url, kGetContactsUpdatedMinParam, time_rfc3339); | |
| 981 } | |
| 982 return url; | |
| 983 } | |
| 984 | |
| 985 //========================== GetContactPhotoOperation ========================== | |
| 986 | |
| 987 GetContactPhotoOperation::GetContactPhotoOperation( | |
| 988 OperationRegistry* registry, | |
| 989 const GURL& photo_url, | |
| 990 const GetContentCallback& callback) | |
| 991 : UrlFetchOperationBase(registry), | |
| 992 photo_url_(photo_url), | |
| 993 callback_(callback) { | |
| 994 } | |
| 995 | |
| 996 GetContactPhotoOperation::~GetContactPhotoOperation() {} | |
| 997 | |
| 998 GURL GetContactPhotoOperation::GetURL() const { | |
| 999 return photo_url_; | |
| 1000 } | |
| 1001 | |
| 1002 void GetContactPhotoOperation::ProcessURLFetchResults( | |
| 1003 const net::URLFetcher* source) { | |
| 1004 GDataErrorCode code = static_cast<GDataErrorCode>(source->GetResponseCode()); | |
| 1005 scoped_ptr<std::string> data(new std::string); | |
| 1006 source->GetResponseAsString(data.get()); | |
| 1007 callback_.Run(code, data.Pass()); | |
| 1008 OnProcessURLFetchResultsComplete(code == HTTP_SUCCESS); | |
| 1009 } | |
| 1010 | |
| 1011 void GetContactPhotoOperation::RunCallbackOnPrematureFailure( | |
| 1012 GDataErrorCode code) { | |
| 1013 scoped_ptr<std::string> data(new std::string); | |
| 1014 callback_.Run(code, data.Pass()); | |
| 1015 } | |
| 1016 | |
| 1017 } // namespace gdata | |
| OLD | NEW |