OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2013 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 <ctype.h> |
| 6 #include <stdio.h> |
| 7 #include <string.h> |
| 8 |
| 9 #include <string> |
| 10 #include <vector> |
| 11 |
| 12 #include "ppapi/c/pp_errors.h" |
| 13 #include "ppapi/cpp/completion_callback.h" |
| 14 #include "ppapi/cpp/instance.h" |
| 15 #include "ppapi/cpp/module.h" |
| 16 #include "ppapi/cpp/url_loader.h" |
| 17 #include "ppapi/cpp/url_request_info.h" |
| 18 #include "ppapi/cpp/url_response_info.h" |
| 19 #include "ppapi/cpp/var.h" |
| 20 #include "ppapi/utility/completion_callback_factory.h" |
| 21 |
| 22 #include "json/reader.h" |
| 23 #include "json/writer.h" |
| 24 |
| 25 static const char kTokenMessage[] = "token:"; |
| 26 static const char kGetFileMessage[] = "getFile"; |
| 27 static const char kBoundary[] = "NACL_BOUNDARY_600673"; |
| 28 |
| 29 |
| 30 static std::string EncodeUriComponent(const std::string& s) { |
| 31 char hex[] = "0123456789ABCDEF"; |
| 32 std::string result; |
| 33 for (size_t i = 0; i < s.length(); ++i) { |
| 34 char c = s[i]; |
| 35 if (isalpha(c) || isdigit(c) || strchr("-_.!~*'()", c)) { |
| 36 result += c; |
| 37 } else { |
| 38 result += '%'; |
| 39 result += hex[(c >> 4) & 0xf]; |
| 40 result += hex[c & 0xf]; |
| 41 } |
| 42 } |
| 43 return result; |
| 44 } |
| 45 |
| 46 static std::string IntToString(int x) { |
| 47 char buffer[32]; |
| 48 snprintf(&buffer[0], 32, "%d", x); |
| 49 return &buffer[0]; |
| 50 } |
| 51 |
| 52 static void AddQueryParameter(std::string* s, |
| 53 const std::string& key, |
| 54 const std::string& value, |
| 55 bool first) { |
| 56 *s += first ? '?' : '&'; |
| 57 *s += EncodeUriComponent(key); |
| 58 *s += '='; |
| 59 *s += EncodeUriComponent(value); |
| 60 } |
| 61 |
| 62 static void AddQueryParameter(std::string* s, |
| 63 const std::string& key, |
| 64 int value, |
| 65 bool first) { |
| 66 AddQueryParameter(s, key, IntToString(value), first); |
| 67 } |
| 68 |
| 69 static void AddAuthTokenHeader(std::string* s, const std::string& auth_token) { |
| 70 *s += "Authorization: Bearer "; |
| 71 *s += auth_token; |
| 72 *s += "\n"; |
| 73 } |
| 74 |
| 75 static void AddHeader(std::string* s, |
| 76 const char* key, |
| 77 const std::string& value) { |
| 78 *s += key; |
| 79 *s += ": "; |
| 80 *s += value; |
| 81 *s += "\n"; |
| 82 } |
| 83 |
| 84 // |
| 85 // UrlReader |
| 86 // |
| 87 struct UrlReaderParams { |
| 88 std::string url; |
| 89 std::string method; |
| 90 std::string request_headers; |
| 91 std::string request_body; |
| 92 }; |
| 93 |
| 94 class UrlReaderDelegate { |
| 95 public: |
| 96 virtual void OnReadSucceeded(const std::string& output) = 0; |
| 97 virtual void OnReadFailed(int32_t result) = 0; |
| 98 }; |
| 99 |
| 100 class UrlReader { |
| 101 public: |
| 102 static UrlReader* Create(pp::Instance* instance, |
| 103 const UrlReaderParams& params, |
| 104 UrlReaderDelegate* delegate); |
| 105 ~UrlReader(); |
| 106 |
| 107 void Run(); |
| 108 |
| 109 private: |
| 110 UrlReader(pp::Instance* instance, |
| 111 const UrlReaderParams& params, |
| 112 UrlReaderDelegate* delegate); |
| 113 |
| 114 static const int32_t kReadBufferSize = 16 * 1024; |
| 115 |
| 116 void InitRequestInfo(); |
| 117 void OnOpen(int32_t result); |
| 118 void ReadBody(); |
| 119 void OnRead(int32_t result); |
| 120 void AppendDataBytes(int32_t num_bytes); |
| 121 |
| 122 void Success(); |
| 123 void Failure(int32_t result); |
| 124 |
| 125 pp::Instance* instance_; // Weak pointer. |
| 126 pp::URLRequestInfo url_request_; |
| 127 pp::URLLoader url_loader_; |
| 128 pp::CompletionCallbackFactory<UrlReader> cc_factory_; |
| 129 UrlReaderParams params_; |
| 130 UrlReaderDelegate* delegate_; // Weak pointer. |
| 131 |
| 132 uint8_t* buffer_; |
| 133 std::string response_body_; |
| 134 }; |
| 135 |
| 136 // static |
| 137 UrlReader* UrlReader::Create(pp::Instance* instance, |
| 138 const UrlReaderParams& params, |
| 139 UrlReaderDelegate* delegate) { |
| 140 return new UrlReader(instance, params, delegate); |
| 141 } |
| 142 |
| 143 UrlReader::UrlReader(pp::Instance* instance, |
| 144 const UrlReaderParams& params, |
| 145 UrlReaderDelegate* delegate) |
| 146 : instance_(instance), |
| 147 url_request_(instance), |
| 148 url_loader_(instance), |
| 149 cc_factory_(this), |
| 150 params_(params), |
| 151 delegate_(delegate), |
| 152 buffer_(new uint8_t[kReadBufferSize]) {} |
| 153 |
| 154 UrlReader::~UrlReader() { delete[] buffer_; } |
| 155 |
| 156 void UrlReader::Run() { |
| 157 InitRequestInfo(); |
| 158 pp::CompletionCallback cc = cc_factory_.NewCallback(&UrlReader::OnOpen); |
| 159 url_loader_.Open(url_request_, cc); |
| 160 } |
| 161 |
| 162 void UrlReader::InitRequestInfo() { |
| 163 url_request_.SetURL(params_.url); |
| 164 url_request_.SetMethod(params_.method); |
| 165 url_request_.SetHeaders(params_.request_headers); |
| 166 url_request_.SetRecordDownloadProgress(true); |
| 167 if (params_.request_body.size()) { |
| 168 url_request_.AppendDataToBody(params_.request_body.data(), |
| 169 params_.request_body.size()); |
| 170 } |
| 171 } |
| 172 |
| 173 void UrlReader::OnOpen(int32_t result) { |
| 174 if (result != PP_OK) |
| 175 return Failure(result); |
| 176 |
| 177 pp::URLResponseInfo url_response = url_loader_.GetResponseInfo(); |
| 178 if (url_response.GetStatusCode() != 200) |
| 179 return Failure(PP_ERROR_FAILED); |
| 180 |
| 181 int64_t bytes_received = 0; |
| 182 int64_t total_bytes_to_be_received = 0; |
| 183 if (url_loader_.GetDownloadProgress(&bytes_received, |
| 184 &total_bytes_to_be_received)) { |
| 185 if (total_bytes_to_be_received > 0) { |
| 186 response_body_.reserve(total_bytes_to_be_received); |
| 187 } |
| 188 } |
| 189 |
| 190 url_request_.SetRecordDownloadProgress(false); |
| 191 ReadBody(); |
| 192 } |
| 193 |
| 194 void UrlReader::ReadBody() { |
| 195 pp::CompletionCallback cc = |
| 196 cc_factory_.NewOptionalCallback(&UrlReader::OnRead); |
| 197 int32_t result = PP_OK; |
| 198 do { |
| 199 result = url_loader_.ReadResponseBody(buffer_, kReadBufferSize, cc); |
| 200 if (result > 0) { |
| 201 AppendDataBytes(result); |
| 202 } |
| 203 } while (result > 0); |
| 204 |
| 205 if (result != PP_OK_COMPLETIONPENDING) { |
| 206 cc.Run(result); |
| 207 } |
| 208 } |
| 209 |
| 210 void UrlReader::OnRead(int32_t result) { |
| 211 if (result < 0) |
| 212 return Failure(result); |
| 213 |
| 214 if (result == 0) { |
| 215 Success(); |
| 216 } else if (result > 0) { |
| 217 AppendDataBytes(result); |
| 218 ReadBody(); |
| 219 } |
| 220 } |
| 221 |
| 222 void UrlReader::AppendDataBytes(int32_t num_bytes) { |
| 223 num_bytes = num_bytes > kReadBufferSize ? kReadBufferSize : num_bytes; |
| 224 response_body_.insert(response_body_.end(), buffer_, buffer_ + num_bytes); |
| 225 } |
| 226 |
| 227 void UrlReader::Success() { |
| 228 delegate_->OnReadSucceeded(response_body_); |
| 229 delete this; |
| 230 } |
| 231 |
| 232 void UrlReader::Failure(int32_t result) { |
| 233 delegate_->OnReadFailed(result); |
| 234 delete this; |
| 235 } |
| 236 |
| 237 // |
| 238 // FilesList |
| 239 // |
| 240 struct FilesListParams { |
| 241 int max_results; |
| 242 std::string page_token; |
| 243 std::string q; |
| 244 }; |
| 245 |
| 246 class FilesListDelegate { |
| 247 public: |
| 248 virtual void OnFilesListSucceeded(const Json::Value& url) = 0; |
| 249 virtual void OnFilesListFailed(int32_t result) = 0; |
| 250 }; |
| 251 |
| 252 class FilesList : public UrlReaderDelegate { |
| 253 public: |
| 254 static FilesList* Create(pp::Instance* instance, |
| 255 const std::string& auth_token, |
| 256 const FilesListParams& params, |
| 257 FilesListDelegate* delegate); |
| 258 ~FilesList(); |
| 259 |
| 260 void Run(); |
| 261 virtual void OnReadSucceeded(const std::string& output); |
| 262 virtual void OnReadFailed(int32_t result); |
| 263 |
| 264 private: |
| 265 FilesList(pp::Instance* instance, |
| 266 const std::string& auth_token, |
| 267 const FilesListParams& params, |
| 268 FilesListDelegate* delegate); |
| 269 |
| 270 pp::Instance* instance_; // Weak pointer. |
| 271 std::string auth_token_; |
| 272 FilesListParams params_; |
| 273 FilesListDelegate* delegate_; // Weak pointer. |
| 274 }; |
| 275 |
| 276 // static |
| 277 FilesList* FilesList::Create(pp::Instance* instance, |
| 278 const std::string& auth_token, |
| 279 const FilesListParams& params, |
| 280 FilesListDelegate* delegate) { |
| 281 return new FilesList(instance, auth_token, params, delegate); |
| 282 } |
| 283 |
| 284 FilesList::FilesList(pp::Instance* instance, |
| 285 const std::string& auth_token, |
| 286 const FilesListParams& params, |
| 287 FilesListDelegate* delegate) |
| 288 : instance_(instance), |
| 289 auth_token_(auth_token), |
| 290 params_(params), |
| 291 delegate_(delegate) {} |
| 292 |
| 293 FilesList::~FilesList() {} |
| 294 |
| 295 void FilesList::Run() { |
| 296 static const char base_url[] = "https://www.googleapis.com/drive/v2/files"; |
| 297 |
| 298 UrlReaderParams p; |
| 299 p.method = "GET"; |
| 300 p.url = base_url; |
| 301 AddQueryParameter(&p.url, "maxResults", params_.max_results, true); |
| 302 if (params_.page_token.length()) |
| 303 AddQueryParameter(&p.url, "pageToken", params_.page_token, false); |
| 304 AddQueryParameter(&p.url, "q", params_.q, false); |
| 305 AddAuthTokenHeader(&p.request_headers, auth_token_); |
| 306 UrlReader* url_reader = UrlReader::Create(instance_, p, this); |
| 307 url_reader->Run(); |
| 308 } |
| 309 |
| 310 void FilesList::OnReadSucceeded(const std::string& output) { |
| 311 Json::Reader reader(Json::Features::strictMode()); |
| 312 Json::Value value; |
| 313 if (!reader.parse(output, value, false)) { |
| 314 printf("Couldn't parse JSON!\n"); |
| 315 delegate_->OnFilesListFailed(PP_ERROR_FAILED); |
| 316 delete this; |
| 317 } |
| 318 |
| 319 delegate_->OnFilesListSucceeded(value); |
| 320 delete this; |
| 321 } |
| 322 |
| 323 void FilesList::OnReadFailed(int32_t result) { |
| 324 delegate_->OnFilesListFailed(result); |
| 325 delete this; |
| 326 } |
| 327 |
| 328 // |
| 329 // FilesInsert |
| 330 // |
| 331 struct FilesInsertParams { |
| 332 std::string file_id; |
| 333 std::string content; |
| 334 std::string description; |
| 335 std::string mime_type; |
| 336 std::string title; |
| 337 }; |
| 338 |
| 339 class FilesInsertDelegate { |
| 340 public: |
| 341 virtual void OnFilesInsertSucceeded(const Json::Value& url) = 0; |
| 342 virtual void OnFilesInsertFailed(int32_t result) = 0; |
| 343 }; |
| 344 |
| 345 class FilesInsert : public UrlReaderDelegate { |
| 346 public: |
| 347 static FilesInsert* Create(pp::Instance* instance, |
| 348 const std::string& auth_token, |
| 349 const FilesInsertParams& params, |
| 350 FilesInsertDelegate* delegate); |
| 351 ~FilesInsert(); |
| 352 |
| 353 void Run(); |
| 354 virtual void OnReadSucceeded(const std::string& output); |
| 355 virtual void OnReadFailed(int32_t result); |
| 356 |
| 357 private: |
| 358 FilesInsert(pp::Instance* instance, |
| 359 const std::string& auth_token, |
| 360 const FilesInsertParams& params, |
| 361 FilesInsertDelegate* delegate); |
| 362 |
| 363 std::string BuildRequestBody(); |
| 364 |
| 365 pp::Instance* instance_; // Weak pointer. |
| 366 std::string auth_token_; |
| 367 FilesInsertParams params_; |
| 368 FilesInsertDelegate* delegate_; // Weak pointer. |
| 369 }; |
| 370 |
| 371 // static |
| 372 FilesInsert* FilesInsert::Create(pp::Instance* instance, |
| 373 const std::string& auth_token, |
| 374 const FilesInsertParams& params, |
| 375 FilesInsertDelegate* delegate) { |
| 376 return new FilesInsert(instance, auth_token, params, delegate); |
| 377 } |
| 378 |
| 379 FilesInsert::FilesInsert(pp::Instance* instance, |
| 380 const std::string& auth_token, |
| 381 const FilesInsertParams& params, |
| 382 FilesInsertDelegate* delegate) |
| 383 : instance_(instance), |
| 384 auth_token_(auth_token), |
| 385 params_(params), |
| 386 delegate_(delegate) {} |
| 387 |
| 388 FilesInsert::~FilesInsert() {} |
| 389 |
| 390 void FilesInsert::Run() { |
| 391 static const char base_url[] = |
| 392 "https://www.googleapis.com/upload/drive/v2/files"; |
| 393 const char* method = "POST"; |
| 394 |
| 395 UrlReaderParams p; |
| 396 p.url = base_url; |
| 397 |
| 398 // If file_id is defined, we are actually updating an existing file. |
| 399 if (!params_.file_id.empty()) { |
| 400 p.url += "/"; |
| 401 p.url += params_.file_id; |
| 402 p.method = "PUT"; |
| 403 } else { |
| 404 p.method = "POST"; |
| 405 } |
| 406 |
| 407 AddQueryParameter(&p.url, "uploadType", "multipart", true); |
| 408 AddAuthTokenHeader(&p.request_headers, auth_token_); |
| 409 AddHeader(&p.request_headers, |
| 410 "Content-Type", |
| 411 std::string("multipart/related; boundary=") + kBoundary + "\n"); |
| 412 p.request_body = BuildRequestBody(); |
| 413 UrlReader* url_reader = UrlReader::Create(instance_, p, this); |
| 414 url_reader->Run(); |
| 415 } |
| 416 |
| 417 void FilesInsert::OnReadSucceeded(const std::string& output) { |
| 418 Json::Reader reader(Json::Features::strictMode()); |
| 419 Json::Value value; |
| 420 if (!reader.parse(output, value, false)) { |
| 421 printf("Couldn't parse JSON!\n"); |
| 422 delegate_->OnFilesInsertFailed(PP_ERROR_FAILED); |
| 423 delete this; |
| 424 } |
| 425 |
| 426 delegate_->OnFilesInsertSucceeded(value); |
| 427 delete this; |
| 428 } |
| 429 |
| 430 void FilesInsert::OnReadFailed(int32_t result) { |
| 431 delegate_->OnFilesInsertFailed(result); |
| 432 delete this; |
| 433 } |
| 434 |
| 435 std::string FilesInsert::BuildRequestBody() { |
| 436 std::string result; |
| 437 result += "--"; |
| 438 result += kBoundary; |
| 439 result += "\nContent-Type: application/json; charset=UTF-8\n\n"; |
| 440 |
| 441 Json::Value value(Json::objectValue); |
| 442 value["description"] = Json::Value(params_.description); |
| 443 value["mimeType"] = Json::Value(params_.mime_type); |
| 444 value["title"] = Json::Value(params_.title); |
| 445 Json::FastWriter writer; |
| 446 std::string metadata = writer.write(value); |
| 447 |
| 448 result += metadata; |
| 449 result += "--"; |
| 450 result += kBoundary; |
| 451 result += "\nContent-Type: "; |
| 452 result += params_.mime_type; |
| 453 result += "\n\n"; |
| 454 result += params_.content; |
| 455 result += "\n--"; |
| 456 result += kBoundary; |
| 457 result += "--"; |
| 458 return result; |
| 459 } |
| 460 |
| 461 // |
| 462 // Instance |
| 463 // |
| 464 class Instance : public pp::Instance, |
| 465 public UrlReaderDelegate, |
| 466 public FilesListDelegate, |
| 467 public FilesInsertDelegate { |
| 468 public: |
| 469 explicit Instance(PP_Instance instance); |
| 470 virtual ~Instance(); |
| 471 virtual void HandleMessage(const pp::Var& var_message); |
| 472 |
| 473 virtual void OnFilesListSucceeded(const Json::Value& root); |
| 474 virtual void OnFilesListFailed(int32_t result); |
| 475 |
| 476 virtual void OnReadSucceeded(const std::string& output); |
| 477 virtual void OnReadFailed(int32_t result); |
| 478 |
| 479 virtual void OnFilesInsertSucceeded(const Json::Value& root); |
| 480 virtual void OnFilesInsertFailed(int32_t result); |
| 481 |
| 482 private: |
| 483 bool GetFirstItemStringValue(const Json::Value& root, |
| 484 const char* key, |
| 485 std::string* out); |
| 486 void GetFileMetadata(const char* title); |
| 487 void DownloadFile(const std::string& download_url); |
| 488 void InsertFile(const char* title, |
| 489 const char* description, |
| 490 const char* content); |
| 491 |
| 492 std::string auth_token_; |
| 493 }; |
| 494 |
| 495 Instance::Instance(PP_Instance instance) : pp::Instance(instance) {} |
| 496 |
| 497 Instance::~Instance() {} |
| 498 |
| 499 void Instance::HandleMessage(const pp::Var& var_message) { |
| 500 if (!var_message.is_string()) { |
| 501 return; |
| 502 } |
| 503 |
| 504 std::string message = var_message.AsString(); |
| 505 if (!strncmp(message.c_str(), kTokenMessage, strlen(kTokenMessage))) { |
| 506 // Auth token |
| 507 auth_token_ = message.c_str() + strlen(kTokenMessage); |
| 508 printf("Auth token: %s\n", auth_token_.c_str()); |
| 509 } else if (!strncmp( |
| 510 message.c_str(), kGetFileMessage, strlen(kGetFileMessage))) { |
| 511 // getFile |
| 512 printf("Getting file...\n"); |
| 513 GetFileMetadata("hello nacl.txt"); |
| 514 } |
| 515 } |
| 516 |
| 517 void Instance::OnFilesListSucceeded(const Json::Value& root) { |
| 518 // Extract the download URL... |
| 519 std::string download_url; |
| 520 if (GetFirstItemStringValue(root, "downloadUrl", &download_url)) { |
| 521 // Read the file... |
| 522 printf("got a download url: %s\n", download_url.c_str()); |
| 523 DownloadFile(download_url); |
| 524 } else { |
| 525 // Insert a new file instead! |
| 526 InsertFile( |
| 527 "hello nacl.txt", "A file generated by NaCl!", "Hello, Google Drive!"); |
| 528 } |
| 529 } |
| 530 |
| 531 void Instance::OnFilesListFailed(int32_t result) { |
| 532 printf("drive.files.list failed with result %d\n", result); |
| 533 } |
| 534 |
| 535 void Instance::OnReadSucceeded(const std::string& output) { |
| 536 PostMessage(output); |
| 537 } |
| 538 |
| 539 void Instance::OnReadFailed(int32_t result) { |
| 540 printf("Reading download URL failed with result %d\n", result); |
| 541 } |
| 542 |
| 543 void Instance::OnFilesInsertSucceeded(const Json::Value& root) { |
| 544 // Extract the download URL... |
| 545 std::string download_url; |
| 546 if (GetFirstItemStringValue(root, "downloadUrl", &download_url)) { |
| 547 // Read the file... |
| 548 printf("got a download url: %s\n", download_url.c_str()); |
| 549 DownloadFile(download_url); |
| 550 } else { |
| 551 printf("Inserted a file, but it doesn't have a downloadUrl?\n"); |
| 552 } |
| 553 } |
| 554 |
| 555 void Instance::OnFilesInsertFailed(int32_t result) { |
| 556 printf("Inserting file failed with result %d\n", result); |
| 557 } |
| 558 |
| 559 bool Instance::GetFirstItemStringValue(const Json::Value& root, |
| 560 const char* key, |
| 561 std::string* out) { |
| 562 if (!root.isMember("items")) { |
| 563 printf("No items...\n"); |
| 564 return false; |
| 565 } |
| 566 |
| 567 Json::Value items = root["items"]; |
| 568 if (!items.isValidIndex(0)) { |
| 569 printf("Expected items[0] to be valid.\n"); |
| 570 return false; |
| 571 } |
| 572 |
| 573 Json::Value first_item = items[0U]; |
| 574 if (!first_item.isMember(key)) { |
| 575 printf("Expected items[0].%s to be valid.\n", key); |
| 576 return false; |
| 577 } |
| 578 |
| 579 Json::Value value = first_item[key]; |
| 580 if (!value.isString()) { |
| 581 printf("Expected items[0].%s to be a string.\n", key); |
| 582 return false; |
| 583 } |
| 584 |
| 585 *out = value.asString(); |
| 586 return true; |
| 587 } |
| 588 |
| 589 void Instance::GetFileMetadata(const char* filename) { |
| 590 FilesListParams p; |
| 591 p.max_results = 1; |
| 592 p.q = "title = \'"; |
| 593 p.q += filename; |
| 594 p.q += "\'"; |
| 595 FilesList* func = FilesList::Create(this, auth_token_, p, this); |
| 596 func->Run(); |
| 597 } |
| 598 |
| 599 void Instance::InsertFile(const char* title, |
| 600 const char* description, |
| 601 const char* content) { |
| 602 FilesInsertParams p; |
| 603 p.content = content; |
| 604 p.description = description; |
| 605 p.mime_type = "text/plain"; |
| 606 p.title = title; |
| 607 FilesInsert* func = FilesInsert::Create(this, auth_token_, p, this); |
| 608 func->Run(); |
| 609 } |
| 610 |
| 611 void Instance::DownloadFile(const std::string& download_url) { |
| 612 UrlReaderParams p; |
| 613 p.method = "GET"; |
| 614 p.url = download_url; |
| 615 AddAuthTokenHeader(&p.request_headers, auth_token_); |
| 616 UrlReader* url_reader = UrlReader::Create(this, p, this); |
| 617 url_reader->Run(); |
| 618 } |
| 619 |
| 620 class Module : public pp::Module { |
| 621 public: |
| 622 Module() : pp::Module() {} |
| 623 virtual ~Module() {} |
| 624 |
| 625 virtual pp::Instance* CreateInstance(PP_Instance instance) { |
| 626 return new Instance(instance); |
| 627 } |
| 628 }; |
| 629 |
| 630 namespace pp { |
| 631 |
| 632 Module* CreateModule() { return new ::Module(); } |
| 633 |
| 634 } // namespace pp |
OLD | NEW |