Index: native_client_sdk/src/examples/demo/drive/drive.cc |
diff --git a/native_client_sdk/src/examples/demo/drive/drive.cc b/native_client_sdk/src/examples/demo/drive/drive.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..6b1d44ad08c98a1fec2ebadddb4f92b993dd2489 |
--- /dev/null |
+++ b/native_client_sdk/src/examples/demo/drive/drive.cc |
@@ -0,0 +1,634 @@ |
+// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include <ctype.h> |
+#include <stdio.h> |
+#include <string.h> |
+ |
+#include <string> |
+#include <vector> |
+ |
+#include "ppapi/c/pp_errors.h" |
+#include "ppapi/cpp/completion_callback.h" |
+#include "ppapi/cpp/instance.h" |
+#include "ppapi/cpp/module.h" |
+#include "ppapi/cpp/url_loader.h" |
+#include "ppapi/cpp/url_request_info.h" |
+#include "ppapi/cpp/url_response_info.h" |
+#include "ppapi/cpp/var.h" |
+#include "ppapi/utility/completion_callback_factory.h" |
+ |
+#include "json/reader.h" |
+#include "json/writer.h" |
+ |
+static const char kTokenMessage[] = "token:"; |
+static const char kGetFileMessage[] = "getFile"; |
+static const char kBoundary[] = "NACL_BOUNDARY_600673"; |
+ |
+ |
+static std::string EncodeUriComponent(const std::string& s) { |
+ char hex[] = "0123456789ABCDEF"; |
+ std::string result; |
+ for (size_t i = 0; i < s.length(); ++i) { |
+ char c = s[i]; |
+ if (isalpha(c) || isdigit(c) || strchr("-_.!~*'()", c)) { |
+ result += c; |
+ } else { |
+ result += '%'; |
+ result += hex[(c >> 4) & 0xf]; |
+ result += hex[c & 0xf]; |
+ } |
+ } |
+ return result; |
+} |
+ |
+static std::string IntToString(int x) { |
+ char buffer[32]; |
+ snprintf(&buffer[0], 32, "%d", x); |
+ return &buffer[0]; |
+} |
+ |
+static void AddQueryParameter(std::string* s, |
+ const std::string& key, |
+ const std::string& value, |
+ bool first) { |
+ *s += first ? '?' : '&'; |
+ *s += EncodeUriComponent(key); |
+ *s += '='; |
+ *s += EncodeUriComponent(value); |
+} |
+ |
+static void AddQueryParameter(std::string* s, |
+ const std::string& key, |
+ int value, |
+ bool first) { |
+ AddQueryParameter(s, key, IntToString(value), first); |
+} |
+ |
+static void AddAuthTokenHeader(std::string* s, const std::string& auth_token) { |
+ *s += "Authorization: Bearer "; |
+ *s += auth_token; |
+ *s += "\n"; |
+} |
+ |
+static void AddHeader(std::string* s, |
+ const char* key, |
+ const std::string& value) { |
+ *s += key; |
+ *s += ": "; |
+ *s += value; |
+ *s += "\n"; |
+} |
+ |
+// |
+// UrlReader |
+// |
+struct UrlReaderParams { |
+ std::string url; |
+ std::string method; |
+ std::string request_headers; |
+ std::string request_body; |
+}; |
+ |
+class UrlReaderDelegate { |
+ public: |
+ virtual void OnReadSucceeded(const std::string& output) = 0; |
+ virtual void OnReadFailed(int32_t result) = 0; |
+}; |
+ |
+class UrlReader { |
+ public: |
+ static UrlReader* Create(pp::Instance* instance, |
+ const UrlReaderParams& params, |
+ UrlReaderDelegate* delegate); |
+ ~UrlReader(); |
+ |
+ void Run(); |
+ |
+ private: |
+ UrlReader(pp::Instance* instance, |
+ const UrlReaderParams& params, |
+ UrlReaderDelegate* delegate); |
+ |
+ static const int32_t kReadBufferSize = 16 * 1024; |
+ |
+ void InitRequestInfo(); |
+ void OnOpen(int32_t result); |
+ void ReadBody(); |
+ void OnRead(int32_t result); |
+ void AppendDataBytes(int32_t num_bytes); |
+ |
+ void Success(); |
+ void Failure(int32_t result); |
+ |
+ pp::Instance* instance_; // Weak pointer. |
+ pp::URLRequestInfo url_request_; |
+ pp::URLLoader url_loader_; |
+ pp::CompletionCallbackFactory<UrlReader> cc_factory_; |
+ UrlReaderParams params_; |
+ UrlReaderDelegate* delegate_; // Weak pointer. |
+ |
+ uint8_t* buffer_; |
+ std::string response_body_; |
+}; |
+ |
+// static |
+UrlReader* UrlReader::Create(pp::Instance* instance, |
+ const UrlReaderParams& params, |
+ UrlReaderDelegate* delegate) { |
+ return new UrlReader(instance, params, delegate); |
+} |
+ |
+UrlReader::UrlReader(pp::Instance* instance, |
+ const UrlReaderParams& params, |
+ UrlReaderDelegate* delegate) |
+ : instance_(instance), |
+ url_request_(instance), |
+ url_loader_(instance), |
+ cc_factory_(this), |
+ params_(params), |
+ delegate_(delegate), |
+ buffer_(new uint8_t[kReadBufferSize]) {} |
+ |
+UrlReader::~UrlReader() { delete[] buffer_; } |
+ |
+void UrlReader::Run() { |
+ InitRequestInfo(); |
+ pp::CompletionCallback cc = cc_factory_.NewCallback(&UrlReader::OnOpen); |
+ url_loader_.Open(url_request_, cc); |
+} |
+ |
+void UrlReader::InitRequestInfo() { |
+ url_request_.SetURL(params_.url); |
+ url_request_.SetMethod(params_.method); |
+ url_request_.SetHeaders(params_.request_headers); |
+ url_request_.SetRecordDownloadProgress(true); |
+ if (params_.request_body.size()) { |
+ url_request_.AppendDataToBody(params_.request_body.data(), |
+ params_.request_body.size()); |
+ } |
+} |
+ |
+void UrlReader::OnOpen(int32_t result) { |
+ if (result != PP_OK) |
+ return Failure(result); |
+ |
+ pp::URLResponseInfo url_response = url_loader_.GetResponseInfo(); |
+ if (url_response.GetStatusCode() != 200) |
+ return Failure(PP_ERROR_FAILED); |
+ |
+ int64_t bytes_received = 0; |
+ int64_t total_bytes_to_be_received = 0; |
+ if (url_loader_.GetDownloadProgress(&bytes_received, |
+ &total_bytes_to_be_received)) { |
+ if (total_bytes_to_be_received > 0) { |
+ response_body_.reserve(total_bytes_to_be_received); |
+ } |
+ } |
+ |
+ url_request_.SetRecordDownloadProgress(false); |
+ ReadBody(); |
+} |
+ |
+void UrlReader::ReadBody() { |
+ pp::CompletionCallback cc = |
+ cc_factory_.NewOptionalCallback(&UrlReader::OnRead); |
+ int32_t result = PP_OK; |
+ do { |
+ result = url_loader_.ReadResponseBody(buffer_, kReadBufferSize, cc); |
+ if (result > 0) { |
+ AppendDataBytes(result); |
+ } |
+ } while (result > 0); |
+ |
+ if (result != PP_OK_COMPLETIONPENDING) { |
+ cc.Run(result); |
+ } |
+} |
+ |
+void UrlReader::OnRead(int32_t result) { |
+ if (result < 0) |
+ return Failure(result); |
+ |
+ if (result == 0) { |
+ Success(); |
+ } else if (result > 0) { |
+ AppendDataBytes(result); |
+ ReadBody(); |
+ } |
+} |
+ |
+void UrlReader::AppendDataBytes(int32_t num_bytes) { |
+ num_bytes = num_bytes > kReadBufferSize ? kReadBufferSize : num_bytes; |
+ response_body_.insert(response_body_.end(), buffer_, buffer_ + num_bytes); |
+} |
+ |
+void UrlReader::Success() { |
+ delegate_->OnReadSucceeded(response_body_); |
+ delete this; |
+} |
+ |
+void UrlReader::Failure(int32_t result) { |
+ delegate_->OnReadFailed(result); |
+ delete this; |
+} |
+ |
+// |
+// FilesList |
+// |
+struct FilesListParams { |
+ int max_results; |
+ std::string page_token; |
+ std::string q; |
+}; |
+ |
+class FilesListDelegate { |
+ public: |
+ virtual void OnFilesListSucceeded(const Json::Value& url) = 0; |
+ virtual void OnFilesListFailed(int32_t result) = 0; |
+}; |
+ |
+class FilesList : public UrlReaderDelegate { |
+ public: |
+ static FilesList* Create(pp::Instance* instance, |
+ const std::string& auth_token, |
+ const FilesListParams& params, |
+ FilesListDelegate* delegate); |
+ ~FilesList(); |
+ |
+ void Run(); |
+ virtual void OnReadSucceeded(const std::string& output); |
+ virtual void OnReadFailed(int32_t result); |
+ |
+ private: |
+ FilesList(pp::Instance* instance, |
+ const std::string& auth_token, |
+ const FilesListParams& params, |
+ FilesListDelegate* delegate); |
+ |
+ pp::Instance* instance_; // Weak pointer. |
+ std::string auth_token_; |
+ FilesListParams params_; |
+ FilesListDelegate* delegate_; // Weak pointer. |
+}; |
+ |
+// static |
+FilesList* FilesList::Create(pp::Instance* instance, |
+ const std::string& auth_token, |
+ const FilesListParams& params, |
+ FilesListDelegate* delegate) { |
+ return new FilesList(instance, auth_token, params, delegate); |
+} |
+ |
+FilesList::FilesList(pp::Instance* instance, |
+ const std::string& auth_token, |
+ const FilesListParams& params, |
+ FilesListDelegate* delegate) |
+ : instance_(instance), |
+ auth_token_(auth_token), |
+ params_(params), |
+ delegate_(delegate) {} |
+ |
+FilesList::~FilesList() {} |
+ |
+void FilesList::Run() { |
+ static const char base_url[] = "https://www.googleapis.com/drive/v2/files"; |
+ |
+ UrlReaderParams p; |
+ p.method = "GET"; |
+ p.url = base_url; |
+ AddQueryParameter(&p.url, "maxResults", params_.max_results, true); |
+ if (params_.page_token.length()) |
+ AddQueryParameter(&p.url, "pageToken", params_.page_token, false); |
+ AddQueryParameter(&p.url, "q", params_.q, false); |
+ AddAuthTokenHeader(&p.request_headers, auth_token_); |
+ UrlReader* url_reader = UrlReader::Create(instance_, p, this); |
+ url_reader->Run(); |
+} |
+ |
+void FilesList::OnReadSucceeded(const std::string& output) { |
+ Json::Reader reader(Json::Features::strictMode()); |
+ Json::Value value; |
+ if (!reader.parse(output, value, false)) { |
+ printf("Couldn't parse JSON!\n"); |
+ delegate_->OnFilesListFailed(PP_ERROR_FAILED); |
+ delete this; |
+ } |
+ |
+ delegate_->OnFilesListSucceeded(value); |
+ delete this; |
+} |
+ |
+void FilesList::OnReadFailed(int32_t result) { |
+ delegate_->OnFilesListFailed(result); |
+ delete this; |
+} |
+ |
+// |
+// FilesInsert |
+// |
+struct FilesInsertParams { |
+ std::string file_id; |
+ std::string content; |
+ std::string description; |
+ std::string mime_type; |
+ std::string title; |
+}; |
+ |
+class FilesInsertDelegate { |
+ public: |
+ virtual void OnFilesInsertSucceeded(const Json::Value& url) = 0; |
+ virtual void OnFilesInsertFailed(int32_t result) = 0; |
+}; |
+ |
+class FilesInsert : public UrlReaderDelegate { |
+ public: |
+ static FilesInsert* Create(pp::Instance* instance, |
+ const std::string& auth_token, |
+ const FilesInsertParams& params, |
+ FilesInsertDelegate* delegate); |
+ ~FilesInsert(); |
+ |
+ void Run(); |
+ virtual void OnReadSucceeded(const std::string& output); |
+ virtual void OnReadFailed(int32_t result); |
+ |
+ private: |
+ FilesInsert(pp::Instance* instance, |
+ const std::string& auth_token, |
+ const FilesInsertParams& params, |
+ FilesInsertDelegate* delegate); |
+ |
+ std::string BuildRequestBody(); |
+ |
+ pp::Instance* instance_; // Weak pointer. |
+ std::string auth_token_; |
+ FilesInsertParams params_; |
+ FilesInsertDelegate* delegate_; // Weak pointer. |
+}; |
+ |
+// static |
+FilesInsert* FilesInsert::Create(pp::Instance* instance, |
+ const std::string& auth_token, |
+ const FilesInsertParams& params, |
+ FilesInsertDelegate* delegate) { |
+ return new FilesInsert(instance, auth_token, params, delegate); |
+} |
+ |
+FilesInsert::FilesInsert(pp::Instance* instance, |
+ const std::string& auth_token, |
+ const FilesInsertParams& params, |
+ FilesInsertDelegate* delegate) |
+ : instance_(instance), |
+ auth_token_(auth_token), |
+ params_(params), |
+ delegate_(delegate) {} |
+ |
+FilesInsert::~FilesInsert() {} |
+ |
+void FilesInsert::Run() { |
+ static const char base_url[] = |
+ "https://www.googleapis.com/upload/drive/v2/files"; |
+ const char* method = "POST"; |
+ |
+ UrlReaderParams p; |
+ p.url = base_url; |
+ |
+ // If file_id is defined, we are actually updating an existing file. |
+ if (!params_.file_id.empty()) { |
+ p.url += "/"; |
+ p.url += params_.file_id; |
+ p.method = "PUT"; |
+ } else { |
+ p.method = "POST"; |
+ } |
+ |
+ AddQueryParameter(&p.url, "uploadType", "multipart", true); |
+ AddAuthTokenHeader(&p.request_headers, auth_token_); |
+ AddHeader(&p.request_headers, |
+ "Content-Type", |
+ std::string("multipart/related; boundary=") + kBoundary + "\n"); |
+ p.request_body = BuildRequestBody(); |
+ UrlReader* url_reader = UrlReader::Create(instance_, p, this); |
+ url_reader->Run(); |
+} |
+ |
+void FilesInsert::OnReadSucceeded(const std::string& output) { |
+ Json::Reader reader(Json::Features::strictMode()); |
+ Json::Value value; |
+ if (!reader.parse(output, value, false)) { |
+ printf("Couldn't parse JSON!\n"); |
+ delegate_->OnFilesInsertFailed(PP_ERROR_FAILED); |
+ delete this; |
+ } |
+ |
+ delegate_->OnFilesInsertSucceeded(value); |
+ delete this; |
+} |
+ |
+void FilesInsert::OnReadFailed(int32_t result) { |
+ delegate_->OnFilesInsertFailed(result); |
+ delete this; |
+} |
+ |
+std::string FilesInsert::BuildRequestBody() { |
+ std::string result; |
+ result += "--"; |
+ result += kBoundary; |
+ result += "\nContent-Type: application/json; charset=UTF-8\n\n"; |
+ |
+ Json::Value value(Json::objectValue); |
+ value["description"] = Json::Value(params_.description); |
+ value["mimeType"] = Json::Value(params_.mime_type); |
+ value["title"] = Json::Value(params_.title); |
+ Json::FastWriter writer; |
+ std::string metadata = writer.write(value); |
+ |
+ result += metadata; |
+ result += "--"; |
+ result += kBoundary; |
+ result += "\nContent-Type: "; |
+ result += params_.mime_type; |
+ result += "\n\n"; |
+ result += params_.content; |
+ result += "\n--"; |
+ result += kBoundary; |
+ result += "--"; |
+ return result; |
+} |
+ |
+// |
+// Instance |
+// |
+class Instance : public pp::Instance, |
+ public UrlReaderDelegate, |
+ public FilesListDelegate, |
+ public FilesInsertDelegate { |
+ public: |
+ explicit Instance(PP_Instance instance); |
+ virtual ~Instance(); |
+ virtual void HandleMessage(const pp::Var& var_message); |
+ |
+ virtual void OnFilesListSucceeded(const Json::Value& root); |
+ virtual void OnFilesListFailed(int32_t result); |
+ |
+ virtual void OnReadSucceeded(const std::string& output); |
+ virtual void OnReadFailed(int32_t result); |
+ |
+ virtual void OnFilesInsertSucceeded(const Json::Value& root); |
+ virtual void OnFilesInsertFailed(int32_t result); |
+ |
+ private: |
+ bool GetFirstItemStringValue(const Json::Value& root, |
+ const char* key, |
+ std::string* out); |
+ void GetFileMetadata(const char* title); |
+ void DownloadFile(const std::string& download_url); |
+ void InsertFile(const char* title, |
+ const char* description, |
+ const char* content); |
+ |
+ std::string auth_token_; |
+}; |
+ |
+Instance::Instance(PP_Instance instance) : pp::Instance(instance) {} |
+ |
+Instance::~Instance() {} |
+ |
+void Instance::HandleMessage(const pp::Var& var_message) { |
+ if (!var_message.is_string()) { |
+ return; |
+ } |
+ |
+ std::string message = var_message.AsString(); |
+ if (!strncmp(message.c_str(), kTokenMessage, strlen(kTokenMessage))) { |
+ // Auth token |
+ auth_token_ = message.c_str() + strlen(kTokenMessage); |
+ printf("Auth token: %s\n", auth_token_.c_str()); |
+ } else if (!strncmp( |
+ message.c_str(), kGetFileMessage, strlen(kGetFileMessage))) { |
+ // getFile |
+ printf("Getting file...\n"); |
+ GetFileMetadata("hello nacl.txt"); |
+ } |
+} |
+ |
+void Instance::OnFilesListSucceeded(const Json::Value& root) { |
+ // Extract the download URL... |
+ std::string download_url; |
+ if (GetFirstItemStringValue(root, "downloadUrl", &download_url)) { |
+ // Read the file... |
+ printf("got a download url: %s\n", download_url.c_str()); |
+ DownloadFile(download_url); |
+ } else { |
+ // Insert a new file instead! |
+ InsertFile( |
+ "hello nacl.txt", "A file generated by NaCl!", "Hello, Google Drive!"); |
+ } |
+} |
+ |
+void Instance::OnFilesListFailed(int32_t result) { |
+ printf("drive.files.list failed with result %d\n", result); |
+} |
+ |
+void Instance::OnReadSucceeded(const std::string& output) { |
+ PostMessage(output); |
+} |
+ |
+void Instance::OnReadFailed(int32_t result) { |
+ printf("Reading download URL failed with result %d\n", result); |
+} |
+ |
+void Instance::OnFilesInsertSucceeded(const Json::Value& root) { |
+ // Extract the download URL... |
+ std::string download_url; |
+ if (GetFirstItemStringValue(root, "downloadUrl", &download_url)) { |
+ // Read the file... |
+ printf("got a download url: %s\n", download_url.c_str()); |
+ DownloadFile(download_url); |
+ } else { |
+ printf("Inserted a file, but it doesn't have a downloadUrl?\n"); |
+ } |
+} |
+ |
+void Instance::OnFilesInsertFailed(int32_t result) { |
+ printf("Inserting file failed with result %d\n", result); |
+} |
+ |
+bool Instance::GetFirstItemStringValue(const Json::Value& root, |
+ const char* key, |
+ std::string* out) { |
+ if (!root.isMember("items")) { |
+ printf("No items...\n"); |
+ return false; |
+ } |
+ |
+ Json::Value items = root["items"]; |
+ if (!items.isValidIndex(0)) { |
+ printf("Expected items[0] to be valid.\n"); |
+ return false; |
+ } |
+ |
+ Json::Value first_item = items[0U]; |
+ if (!first_item.isMember(key)) { |
+ printf("Expected items[0].%s to be valid.\n", key); |
+ return false; |
+ } |
+ |
+ Json::Value value = first_item[key]; |
+ if (!value.isString()) { |
+ printf("Expected items[0].%s to be a string.\n", key); |
+ return false; |
+ } |
+ |
+ *out = value.asString(); |
+ return true; |
+} |
+ |
+void Instance::GetFileMetadata(const char* filename) { |
+ FilesListParams p; |
+ p.max_results = 1; |
+ p.q = "title = \'"; |
+ p.q += filename; |
+ p.q += "\'"; |
+ FilesList* func = FilesList::Create(this, auth_token_, p, this); |
+ func->Run(); |
+} |
+ |
+void Instance::InsertFile(const char* title, |
+ const char* description, |
+ const char* content) { |
+ FilesInsertParams p; |
+ p.content = content; |
+ p.description = description; |
+ p.mime_type = "text/plain"; |
+ p.title = title; |
+ FilesInsert* func = FilesInsert::Create(this, auth_token_, p, this); |
+ func->Run(); |
+} |
+ |
+void Instance::DownloadFile(const std::string& download_url) { |
+ UrlReaderParams p; |
+ p.method = "GET"; |
+ p.url = download_url; |
+ AddAuthTokenHeader(&p.request_headers, auth_token_); |
+ UrlReader* url_reader = UrlReader::Create(this, p, this); |
+ url_reader->Run(); |
+} |
+ |
+class Module : public pp::Module { |
+ public: |
+ Module() : pp::Module() {} |
+ virtual ~Module() {} |
+ |
+ virtual pp::Instance* CreateInstance(PP_Instance instance) { |
+ return new Instance(instance); |
+ } |
+}; |
+ |
+namespace pp { |
+ |
+Module* CreateModule() { return new ::Module(); } |
+ |
+} // namespace pp |