Index: native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs_util.cc |
diff --git a/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs_util.cc b/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs_util.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..844a51975823d702f7eed36e65b45300df4e6af6 |
--- /dev/null |
+++ b/native_client_sdk/src/libraries/nacl_io/googledrivefs/googledrivefs_util.cc |
@@ -0,0 +1,435 @@ |
+// Copyright (c) 2016 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 "nacl_io/googledrivefs/googledrivefs_util.h" |
+ |
+#include <stdint.h> |
+#include <stdlib.h> |
+#include <string.h> |
+ |
+#include <limits> |
+ |
+#include "ppapi/c/pp_completion_callback.h" |
+ |
+#include "nacl_io/error.h" |
+#include "nacl_io/filesystem.h" |
+#include "nacl_io/path.h" |
+#include "nacl_io/pepper_interface.h" |
+#include "nacl_io/statuscode.h" |
+#include "nacl_io/googledrivefs/googledrivefs.h" |
+ |
+namespace nacl_io { |
+ |
+#define ARRAY_SIZE_UNSAFE(arr) ((sizeof arr) / sizeof arr[0]) |
+ |
+const char FOLDER_MIME_TYPE[] = "application/vnd.google-apps.folder"; |
+ |
+const char DRIVE_URL[] = "https://www.googleapis.com/drive/v3/files"; |
+const char UPLOAD_DRIVE_URL[] = |
+ "https://www.googleapis.com/upload/drive/v3/files"; |
+const char DOWNLOAD_DRIVE_URL[] = |
+ "https://www.googleapis.com/download/drive/v3/files"; |
+ |
+std::string ParentEqualClause(const std::string& parent_dir_id) { |
+ return "%27" + parent_dir_id + "%27+in+parents"; |
+} |
+ |
+std::string NameEqualClause(const std::string& name) { |
+ return "name+=+%27" + name + "%27"; |
+} |
+ |
+int ExtractYearFromRFC3339(const std::string& date_time) { |
+ return atoi(date_time.substr(0, 4).c_str()); |
+} |
+ |
+int ExtractMonthFromRFC3339(const std::string& date_time) { |
+ return atoi(date_time.substr(5, 2).c_str()); |
+} |
+ |
+int ExtractDayFromRFC3339(const std::string& date_time) { |
+ return atoi(date_time.substr(8, 2).c_str()); |
+} |
+ |
+int ExtractHourFromRFC3339(const std::string& date_time) { |
+ return atoi(date_time.substr(11, 2).c_str()); |
+} |
+ |
+int ExtractMinuteFromRFC3339(const std::string& date_time) { |
+ return atoi(date_time.substr(14, 2).c_str()); |
+} |
+ |
+int ExtractSecondFromRFC3339(const std::string& date_time) { |
+ return atoi(date_time.substr(17, 2).c_str()); |
+} |
+ |
+time_t ConvertDateTimeToEpochTime(int year, |
+ int month, |
+ int day, |
+ int hour, |
+ int minute, |
+ int second) { |
+ time_t epoch_time = 0; |
+ |
+ for (int i = 1970; i < year; ++i) { |
+ if (i % 4 == 0 && (i % 100 != 0 || i % 400 == 0)) { |
+ epoch_time += 366 * 24 * 60 * 60; |
+ } else { |
+ epoch_time += 365 * 24 * 60 * 60; |
+ } |
+ } |
+ int month_to_number_of_days[] = {31, 28, 31, 30, 31, 30, |
+ 31, 31, 30, 31, 30, 31}; |
+ |
+ for (int i = 1; i < month; ++i) { |
+ epoch_time += month_to_number_of_days[i - 1] * 24 * 60 * 60; |
+ if (i == 2 && (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))) { |
+ epoch_time += 24 * 60 * 60; |
+ } |
+ } |
+ |
+ epoch_time += (day - 1) * 24 * 60 * 60; |
+ |
+ if (hour > 0) { |
+ epoch_time += hour * 60 * 60; |
+ } |
+ |
+ if (minute > 0) { |
+ epoch_time += minute * 60; |
+ } |
+ |
+ epoch_time += second; |
+ |
+ return epoch_time; |
+} |
+ |
+void AddUrlPath(const std::string& path, std::string* out_url) { |
+ *out_url += "/"; |
+ *out_url += path; |
+} |
+ |
+void AddUrlQAttributeValue(std::string* array, |
+ size_t size, |
+ std::string* out_q_attribute_value) { |
+ *out_q_attribute_value = array[0]; |
+ for (size_t i = 1; i < size; ++i) { |
+ *out_q_attribute_value += "+and+"; |
+ *out_q_attribute_value += array[i]; |
+ } |
+} |
+ |
+void AddUrlFirstQueryParameter(const std::string& attribute, |
+ const std::string& value, |
+ std::string* out_url) { |
+ *out_url += "?"; |
+ *out_url += attribute; |
+ *out_url += "="; |
+ *out_url += value; |
+} |
+ |
+void AddUrlNextQueryParameter(const std::string& attribute, |
+ const std::string& value, |
+ std::string* out_url) { |
+ *out_url += "&"; |
+ *out_url += attribute; |
+ *out_url += "="; |
+ *out_url += value; |
+} |
+ |
+void AddHeaders(const std::string& header_field_key, |
+ const std::string& header_field_value, |
+ std::string* out_headers) { |
+ *out_headers += header_field_key; |
+ *out_headers += ": "; |
+ *out_headers += header_field_value; |
+ *out_headers += "\n"; |
+} |
+ |
+void AddBody(const std::string& data, std::string* out_body) { |
+ *out_body += data; |
+ *out_body += "\n"; |
+} |
+ |
+Error LoadUrl(PepperInterface* ppapi, |
+ const RequestUrlParams& params, |
+ ScopedResource* out_url_response_info_resource) { |
+ out_url_response_info_resource->Reset(0); |
+ |
+ URLLoaderInterface* url_loader_iface = ppapi->GetURLLoaderInterface(); |
+ ScopedResource url_loader_resource( |
+ ppapi, url_loader_iface->Create(ppapi->GetInstance())); |
+ if (!url_loader_resource.pp_resource()) { |
+ return EPERM; |
+ } |
+ |
+ URLRequestInfoInterface* url_request_info_iface = |
+ ppapi->GetURLRequestInfoInterface(); |
+ ScopedResource url_request_info_resource( |
+ ppapi, url_request_info_iface->Create(ppapi->GetInstance())); |
+ if (!url_request_info_resource.pp_resource()) { |
+ return EPERM; |
+ } |
+ |
+ VarInterface* var_iface = ppapi->GetVarInterface(); |
+ |
+ struct PP_Var pp_var = |
+ var_iface->VarFromUtf8(params.url.c_str(), params.url.size()); |
+ if (!url_request_info_iface->SetProperty( |
+ url_request_info_resource.pp_resource(), PP_URLREQUESTPROPERTY_URL, |
+ pp_var)) { |
+ return EPERM; |
+ } |
+ |
+ pp_var = var_iface->VarFromUtf8(params.method.c_str(), params.method.size()); |
+ if (!url_request_info_iface->SetProperty( |
+ url_request_info_resource.pp_resource(), PP_URLREQUESTPROPERTY_METHOD, |
+ pp_var)) { |
+ return EPERM; |
+ } |
+ |
+ pp_var = |
+ var_iface->VarFromUtf8(params.headers.c_str(), params.headers.size()); |
+ if (!url_request_info_iface->SetProperty( |
+ url_request_info_resource.pp_resource(), |
+ PP_URLREQUESTPROPERTY_HEADERS, pp_var)) { |
+ return EPERM; |
+ } |
+ |
+ if (!url_request_info_iface->AppendDataToBody( |
+ url_request_info_resource.pp_resource(), params.body.data(), |
+ params.body.size())) { |
+ return EPERM; |
+ } |
+ |
+ pp_var = PP_MakeBool(PP_TRUE); |
+ if (!url_request_info_iface->SetProperty( |
+ url_request_info_resource.pp_resource(), |
+ PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS, pp_var)) { |
+ return EPERM; |
+ } |
+ |
+ pp_var = PP_MakeBool(PP_TRUE); |
+ if (!url_request_info_iface->SetProperty( |
+ url_request_info_resource.pp_resource(), |
+ PP_URLREQUESTPROPERTY_STREAMTOFILE, pp_var)) { |
+ return EPERM; |
+ } |
+ |
+ int32_t err = url_loader_iface->Open(url_loader_resource.pp_resource(), |
+ url_request_info_resource.pp_resource(), |
+ PP_BlockUntilComplete()); |
+ if (err < 0) { |
+ return PPERROR_TO_ERRNO(err); |
+ } |
+ |
+ int32_t bytes_read = url_loader_iface->FinishStreamingToFile( |
+ url_loader_resource.pp_resource(), PP_BlockUntilComplete()); |
+ if (bytes_read < 0) { |
+ return PPERROR_TO_ERRNO(bytes_read); |
+ } |
+ |
+ out_url_response_info_resource->Reset( |
+ url_loader_iface->GetResponseInfo(url_loader_resource.pp_resource())); |
+ if (!out_url_response_info_resource->pp_resource()) { |
+ return EPERM; |
+ } |
+ |
+ return 0; |
+} |
+ |
+Error ReadResponseBody(PepperInterface* ppapi, |
+ PP_Resource url_response_info_object, |
+ int32_t bytes_to_read, |
+ std::string* out_output) { |
+ out_output->clear(); |
+ |
+ URLResponseInfoInterface* url_response_info_iface = |
+ ppapi->GetURLResponseInfoInterface(); |
+ FileIoInterface* file_io_iface = ppapi->GetFileIoInterface(); |
+ |
+ ScopedResource file_ref_resource( |
+ ppapi, |
+ url_response_info_iface->GetBodyAsFileRef(url_response_info_object)); |
+ if (!file_ref_resource.pp_resource()) { |
+ return EPERM; |
+ } |
+ |
+ ScopedResource file_io_resource(ppapi, |
+ file_io_iface->Create(ppapi->GetInstance())); |
+ if (!file_io_resource.pp_resource()) { |
+ return EPERM; |
+ } |
+ |
+ int32_t open_result = file_io_iface->Open( |
+ file_io_resource.pp_resource(), file_ref_resource.pp_resource(), |
+ PP_FILEOPENFLAG_READ, PP_BlockUntilComplete()); |
+ if (open_result < 0) { |
+ return PPERROR_TO_ERRNO(open_result); |
+ } |
+ |
+ std::string string_buffer(1024, '\0'); |
+ int32_t bytes_read = |
+ file_io_iface->Read(file_io_resource.pp_resource(), 0, &string_buffer[0], |
+ string_buffer.size(), PP_BlockUntilComplete()); |
+ |
+ while (bytes_read > 0) { |
+ *out_output += string_buffer.substr(0, bytes_read); |
+ |
+ bytes_read = file_io_iface->Read( |
+ file_io_resource.pp_resource(), out_output->size(), &string_buffer[0], |
+ string_buffer.size(), PP_BlockUntilComplete()); |
+ } |
+ |
+ if (bytes_read < 0) { |
+ return PPERROR_TO_ERRNO(bytes_read); |
+ } |
+ |
+ out_output->resize(std::min<size_t>(out_output->size(), bytes_to_read)); |
+ |
+ return 0; |
+} |
+ |
+int32_t ReadStatusCode(PepperInterface* ppapi, |
+ PP_Resource url_response_info_object) { |
+ URLResponseInfoInterface* url_response_info_iface = |
+ ppapi->GetURLResponseInfoInterface(); |
+ |
+ struct PP_Var status_code_var = url_response_info_iface->GetProperty( |
+ url_response_info_object, PP_URLRESPONSEPROPERTY_STATUSCODE); |
+ |
+ return status_code_var.value.as_int; |
+} |
+ |
+Error GetValueStringAndValuePos(const std::string& json, |
+ const std::string& key, |
+ size_t key_search_pos, |
+ std::string* out_value_string, |
+ size_t* out_value_pos) { |
+ out_value_string->clear(); |
+ *out_value_pos = std::string::npos; |
+ |
+ // json format is |
+ // .. |
+ // "<key1>": "<value1>" |
+ // .. |
+ // "<key2>": "<value2>" |
+ // .. |
+ // The variable key_location stores the index of the first character |
+ // of a target key. |
+ const char* s_quote_colon_quote = "\": \""; |
+ size_t key_location = json.find(key + s_quote_colon_quote, key_search_pos); |
+ if (key_location == std::string::npos) { |
+ return EINVAL; |
+ } |
+ |
+ size_t start_value_index = |
+ key_location + key.size() + strlen(s_quote_colon_quote); |
+ size_t end_value_index = json.find("\"", start_value_index); |
+ if (end_value_index == std::string::npos) { |
+ return EPERM; |
+ } |
+ |
+ *out_value_string = |
+ json.substr(start_value_index, end_value_index - start_value_index); |
+ *out_value_pos = start_value_index; |
+ |
+ return 0; |
+} |
+ |
+// A Google Drive item is a file or a directory. |
+// RequestItemIdAndItemType requests to see if either a file |
+// with item_name or a directory with item_name is there. |
+Error RequestItemIdAndItemType(const std::string& parent_dir_id, |
+ const std::string& item_name, |
+ GoogleDriveFs* googledrivefs, |
+ std::string* out_item_id, |
+ bool* out_is_dir_type) { |
+ out_item_id->clear(); |
+ *out_is_dir_type = false; |
+ |
+ RequestUrlParams p; |
+ |
+ p.url = DRIVE_URL; |
+ std::string q_attribute_value; |
+ std::string attribute_values[] = {ParentEqualClause(parent_dir_id), |
+ NameEqualClause(item_name)}; |
+ |
+ AddUrlQAttributeValue(attribute_values, ARRAY_SIZE_UNSAFE(attribute_values), |
+ &q_attribute_value); |
+ AddUrlFirstQueryParameter("q", q_attribute_value, &p.url); |
+ |
+ p.method = "GET"; |
+ |
+ AddHeaders("Content-type", "application/json", &p.headers); |
+ AddHeaders("Authorization", "Bearer " + googledrivefs->token(), &p.headers); |
+ |
+ ScopedResource url_response_info_resource(googledrivefs->ppapi()); |
+ Error error = LoadUrl(googledrivefs->ppapi(), p, &url_response_info_resource); |
+ if (error) { |
+ return error; |
+ } |
+ |
+ if (ReadStatusCode(googledrivefs->ppapi(), |
+ url_response_info_resource.pp_resource()) != |
+ STATUSCODE_OK) { |
+ return EPERM; |
+ } |
+ |
+ std::string output; |
+ error = ReadResponseBody(googledrivefs->ppapi(), |
+ url_response_info_resource.pp_resource(), |
+ std::numeric_limits<int32_t>::max(), &output); |
+ if (error) { |
+ return error; |
+ } |
+ |
+ size_t id_index; |
+ error = GetValueStringAndValuePos(output, "id", 0, out_item_id, &id_index); |
+ if (error == EINVAL) { |
+ return ENOENT; |
+ } else if (error) { |
+ return error; |
+ } |
+ |
+ std::string mime_type_value; |
+ size_t mime_type_index; |
+ error = GetValueStringAndValuePos(output, "mimeType", 0, &mime_type_value, |
+ &mime_type_index); |
+ if (error) { |
+ return EPERM; |
+ } |
+ |
+ *out_is_dir_type = (mime_type_value == FOLDER_MIME_TYPE); |
+ |
+ return 0; |
+} |
+ |
+Error RequestParentDirId(const Path& path, |
+ GoogleDriveFs* googledrivefs, |
+ std::string* out_parent_dir_id) { |
+ out_parent_dir_id->clear(); |
+ |
+ if (path.IsRoot()) { |
+ return EINVAL; |
+ } |
+ |
+ std::string helper_parent_dir_id = "root"; |
+ |
+ for (size_t i = 1; i < path.Size() - 1; ++i) { |
+ std::string dir_name = path.Range(i, i + 1); |
+ std::string dir_id; |
+ bool is_dir_type; |
+ Error error = RequestItemIdAndItemType( |
+ helper_parent_dir_id, dir_name, googledrivefs, &dir_id, &is_dir_type); |
+ if (error || !is_dir_type) { |
+ return EPERM; |
+ } |
+ |
+ helper_parent_dir_id = dir_id; |
+ } |
+ *out_parent_dir_id = helper_parent_dir_id; |
+ |
+ return 0; |
+} |
+ |
+} // namespace nacl_io |