| 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
|
|
|