OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2016 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 "nacl_io/googledrivefs/googledrivefs_util.h" | |
6 | |
7 #include <stdlib.h> | |
8 | |
9 #include "ppapi/c/pp_completion_callback.h" | |
10 #include "ppapi/c/pp_instance.h" | |
11 | |
12 #include "nacl_io/error.h" | |
13 #include "nacl_io/filesystem.h" | |
14 #include "nacl_io/path.h" | |
15 #include "nacl_io/pepper_interface.h" | |
16 #include "nacl_io/statuscode.h" | |
17 #include "nacl_io/googledrivefs/googledrivefs.h" | |
18 | |
19 namespace nacl_io { | |
20 | |
21 void AddUrlPath(const std::string& path, std::string* out_url) { | |
22 *out_url += "/"; | |
23 *out_url += path; | |
24 } | |
25 | |
26 void AddUrlQAttributeValue(std::string* array, | |
27 int size, | |
28 std::string* out_q_attribute_value) { | |
29 *out_q_attribute_value = array[0]; | |
30 for (int i = 1; i < size; ++i) { | |
31 *out_q_attribute_value += "+and+"; | |
32 *out_q_attribute_value += array[i]; | |
33 } | |
34 } | |
35 | |
36 void AddUrlFirstQueryParameter(const std::string& attribute, | |
37 const std::string& value, | |
38 std::string* out_url) { | |
39 *out_url += "?"; | |
40 *out_url += attribute; | |
41 *out_url += "="; | |
42 *out_url += value; | |
43 } | |
44 | |
45 void AddUrlNextQueryParameter(const std::string& attribute, | |
46 const std::string& value, | |
47 std::string* out_url) { | |
48 *out_url += "&"; | |
49 *out_url += attribute; | |
50 *out_url += "="; | |
51 *out_url += value; | |
52 } | |
53 | |
54 void AddHeaders(const std::string& header_field_key, | |
55 const std::string& header_field_value, | |
56 std::string* out_headers) { | |
57 *out_headers += header_field_key; | |
58 *out_headers += ": "; | |
59 *out_headers += header_field_value; | |
60 *out_headers += "\n"; | |
61 } | |
62 | |
63 void AddBody(const std::string& data, std::string* out_body) { | |
64 *out_body += data; | |
65 *out_body += "\n"; | |
66 } | |
67 | |
68 Error FinishPreparingResponse(PepperInterface* ppapi, | |
69 PP_Resource url_loader_object, | |
70 PP_Resource* out_url_response_info_object) { | |
71 *out_url_response_info_object = 0; | |
72 | |
73 URLLoaderInterface* url_loader_iface = ppapi->GetURLLoaderInterface(); | |
74 | |
75 int32_t bytes_read = url_loader_iface->FinishStreamingToFile( | |
76 url_loader_object, PP_BlockUntilComplete()); | |
77 if (bytes_read < 0) { | |
78 return PPERROR_TO_ERRNO(bytes_read); | |
79 } | |
80 | |
81 *out_url_response_info_object = | |
82 url_loader_iface->GetResponseInfo(url_loader_object); | |
83 | |
84 url_loader_iface->Close(url_loader_object); | |
85 | |
86 return 0; | |
87 } | |
88 | |
89 Error MakeRequest(PepperInterface* ppapi, | |
90 PP_Resource url_loader_object, | |
91 PP_Resource url_request_info_object, | |
92 const RequestUrlParams& params) { | |
93 VarInterface* var_iface = ppapi->GetVarInterface(); | |
94 URLLoaderInterface* url_loader_iface = ppapi->GetURLLoaderInterface(); | |
95 URLRequestInfoInterface* url_request_info_iface = | |
96 ppapi->GetURLRequestInfoInterface(); | |
97 | |
98 struct PP_Var pp_var = | |
99 var_iface->VarFromUtf8(params.url.c_str(), params.url.size()); | |
100 if (!url_request_info_iface->SetProperty(url_request_info_object, | |
chanpatorikku
2016/08/07 02:41:03
Lines 100-101 are samples of code that attempts in
| |
101 PP_URLREQUESTPROPERTY_URL, pp_var)) { | |
102 return EPERM; | |
103 } | |
104 | |
105 pp_var = var_iface->VarFromUtf8(params.method.c_str(), params.method.size()); | |
106 if (!url_request_info_iface->SetProperty( | |
107 url_request_info_object, PP_URLREQUESTPROPERTY_METHOD, pp_var)) { | |
108 return EPERM; | |
109 } | |
110 | |
111 pp_var = | |
112 var_iface->VarFromUtf8(params.headers.c_str(), params.headers.size()); | |
113 if (!url_request_info_iface->SetProperty( | |
114 url_request_info_object, PP_URLREQUESTPROPERTY_HEADERS, pp_var)) { | |
115 return EPERM; | |
116 } | |
117 | |
118 if (!params.body.empty() && | |
119 !url_request_info_iface->AppendDataToBody( | |
120 url_request_info_object, params.body.data(), params.body.size())) { | |
121 return EPERM; | |
122 } | |
123 | |
124 pp_var = PP_MakeBool(PP_TRUE); | |
125 if (!url_request_info_iface->SetProperty( | |
126 url_request_info_object, | |
127 PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS, pp_var)) { | |
128 return EPERM; | |
129 } | |
130 | |
131 pp_var = PP_MakeBool(PP_TRUE); | |
132 if (!url_request_info_iface->SetProperty(url_request_info_object, | |
133 PP_URLREQUESTPROPERTY_STREAMTOFILE, | |
134 pp_var)) { | |
135 return EPERM; | |
136 } | |
137 | |
138 int32_t err = url_loader_iface->Open( | |
139 url_loader_object, url_request_info_object, PP_BlockUntilComplete()); | |
140 | |
141 if (err < 0) { | |
142 return PPERROR_TO_ERRNO(err); | |
143 } | |
144 | |
145 return 0; | |
146 } | |
147 | |
148 Error ReadResponseBody(PepperInterface* ppapi, | |
149 PP_Instance instance, | |
150 PP_Resource url_response_info_object, | |
151 int32_t bytes_to_read, | |
152 std::string* out_output) { | |
153 *out_output = ""; | |
154 | |
155 URLResponseInfoInterface* url_response_info_iface = | |
156 ppapi->GetURLResponseInfoInterface(); | |
157 FileIoInterface* file_io_iface = ppapi->GetFileIoInterface(); | |
158 | |
159 PP_Resource file_ref_object = | |
160 url_response_info_iface->GetBodyAsFileRef(url_response_info_object); | |
161 PP_Resource file_io_object = file_io_iface->Create(instance); | |
162 | |
163 Error error(0); | |
164 int32_t bytes_read = 0; | |
165 int BUFFER_SIZE = 1024; | |
166 // Memory is allocated in the heap but not the stack to work around | |
167 // https://bugs.chromium.org/p/nativeclient/issues/detail?id=4114 | |
168 char* buffer = new char[BUFFER_SIZE]; | |
169 | |
170 int32_t open_result = | |
171 file_io_iface->Open(file_io_object, file_ref_object, PP_FILEOPENFLAG_READ, | |
172 PP_BlockUntilComplete()); | |
173 if (open_result < 0) { | |
174 error = PPERROR_TO_ERRNO(open_result); | |
175 goto done; | |
176 } | |
177 | |
178 bytes_read = file_io_iface->Read(file_io_object, 0, buffer, BUFFER_SIZE, | |
179 PP_BlockUntilComplete()); | |
180 | |
181 while (bytes_read > 0) { | |
182 *out_output += std::string(buffer, bytes_read); | |
183 | |
184 bytes_read = file_io_iface->Read(file_io_object, out_output->size(), buffer, | |
185 BUFFER_SIZE, PP_BlockUntilComplete()); | |
186 } | |
187 | |
188 if (bytes_read < 0) { | |
189 error = PPERROR_TO_ERRNO(bytes_read); | |
190 goto done; | |
191 } | |
192 | |
193 out_output->resize(std::min(out_output->size(), (size_t)bytes_to_read)); | |
194 | |
195 done: | |
196 if (file_ref_object) { | |
197 ppapi->ReleaseResource(file_ref_object); | |
198 } | |
199 if (file_io_object) { | |
200 file_io_iface->Close(file_io_object); | |
201 | |
202 ppapi->ReleaseResource(file_io_object); | |
203 } | |
204 | |
205 delete[] buffer; | |
206 | |
207 return error; | |
208 } | |
209 | |
210 int32_t ReadStatusCode(PepperInterface* ppapi, | |
211 PP_Resource url_response_info_object) { | |
212 URLResponseInfoInterface* url_response_info_iface = | |
213 ppapi->GetURLResponseInfoInterface(); | |
214 | |
215 struct PP_Var status_code_var = url_response_info_iface->GetProperty( | |
216 url_response_info_object, PP_URLRESPONSEPROPERTY_STATUSCODE); | |
217 | |
218 return status_code_var.value.as_int; | |
219 } | |
220 | |
221 void GetValue(const std::string& json, | |
222 const std::string& key, | |
223 int find_pos, | |
224 std::string* out_value, | |
225 int* out_value_char_pos) { | |
226 // All keys parsed in the response format in GoogleDriveFs are unique. | |
227 // Suppose keys of the API response stay unchanged. | |
228 // Without parsing Json, use substring search to get the key's value. | |
229 | |
230 *out_value = ""; | |
231 *out_value_char_pos = -1; | |
232 | |
233 size_t key_location = json.find("\"" + key + "\": \"", find_pos); | |
234 if (key_location == std::string::npos) { | |
235 return; | |
binji
2016/08/22 19:21:53
This should be an error.
chanpatorikku
2016/08/29 17:14:03
Done. The function is redesigned to return an erro
| |
236 } | |
237 | |
238 size_t start_value_index = key_location + key.size() + 5; | |
binji
2016/08/22 19:21:53
Why 5?
chanpatorikku
2016/08/29 17:14:03
From your other comments, you already figured out
| |
239 // GetValue is called when STATUSCODE_OK responses are received. | |
240 // The responses are assumed to always be completely received. | |
241 size_t end_value_index = json.find("\"", start_value_index); | |
binji
2016/08/22 19:21:53
If GetValue is always returning a string, you may
chanpatorikku
2016/08/29 17:14:03
GetValue(..) is renamed to GetValueStringAndValueP
| |
242 | |
243 *out_value = | |
244 json.substr(start_value_index, end_value_index - start_value_index); | |
245 *out_value_char_pos = start_value_index; | |
246 } | |
247 | |
248 // A Google Drive item is a file or a directory. | |
249 // RequestDirId requests to see if a directory with dir_name | |
250 // and a parent with ID parent_dir_id is there. | |
251 // If it is, dir_id is set. | |
252 // RequestDirId returns ENOENT when a file with dir_name is there | |
253 // but a directory with dir_name is not. | |
254 Error RequestDirId(const std::string& parent_dir_id, | |
255 const std::string& dir_name, | |
256 Filesystem* filesystem, | |
257 std::string* out_dir_id) { | |
258 *out_dir_id = ""; | |
259 | |
260 GoogleDriveFs* googledrivefs = static_cast<GoogleDriveFs*>(filesystem); | |
261 | |
262 URLLoaderInterface* url_loader_iface = | |
263 googledrivefs->ppapi()->GetURLLoaderInterface(); | |
264 PP_Resource url_loader_object = | |
265 url_loader_iface->Create(googledrivefs->instance()); | |
266 URLRequestInfoInterface* url_request_info_iface = | |
267 googledrivefs->ppapi()->GetURLRequestInfoInterface(); | |
268 PP_Resource url_request_info_object = | |
269 url_request_info_iface->Create(googledrivefs->instance()); | |
270 PP_Resource url_response_info_object = 0; | |
271 Error error(0); | |
272 std::string output; | |
273 int id_index; | |
274 | |
275 static const char base_url[] = "https://www.googleapis.com/drive/v3/files"; | |
276 | |
277 RequestUrlParams p; | |
278 | |
279 p.url = base_url; | |
280 | |
281 std::string q_attribute_value = ""; | |
282 | |
283 std::string attribute_values[] = { | |
284 "%27" + parent_dir_id + "%27+in+parents", | |
285 "mimeType+=+%27application/vnd.google-apps.folder%27", | |
286 "name+=+%27" + dir_name + "%27"}; | |
287 | |
288 AddUrlQAttributeValue(attribute_values, 3, &q_attribute_value); | |
289 AddUrlFirstQueryParameter("q", q_attribute_value, &p.url); | |
290 | |
291 p.method = "GET"; | |
292 | |
293 p.headers = ""; | |
294 | |
295 AddHeaders("Content-type", "application/json", &p.headers); | |
296 AddHeaders("Authorization", "Bearer " + googledrivefs->token(), &p.headers); | |
297 | |
298 error = MakeRequest(googledrivefs->ppapi(), url_loader_object, | |
299 url_request_info_object, p); | |
300 | |
301 if (error) { | |
302 goto done; | |
303 } | |
304 | |
305 error = FinishPreparingResponse(googledrivefs->ppapi(), url_loader_object, | |
306 &url_response_info_object); | |
307 | |
308 if (error) { | |
309 goto done; | |
310 } | |
311 | |
312 if (ReadStatusCode(googledrivefs->ppapi(), url_response_info_object) != | |
313 STATUSCODE_OK) { | |
314 error = EPERM; | |
315 goto done; | |
316 } | |
317 | |
318 error = ReadResponseBody(googledrivefs->ppapi(), googledrivefs->instance(), | |
319 url_response_info_object, INT_MAX, &output); | |
320 | |
321 if (error) { | |
322 goto done; | |
323 } | |
324 | |
325 GetValue(output, "id", 0, out_dir_id, &id_index); | |
326 if (id_index == -1) { | |
327 error = ENOENT; | |
328 goto done; | |
329 } | |
330 | |
331 done: | |
332 if (url_loader_object) { | |
333 googledrivefs->ppapi()->ReleaseResource(url_loader_object); | |
334 } | |
335 if (url_request_info_object) { | |
336 googledrivefs->ppapi()->ReleaseResource(url_request_info_object); | |
337 } | |
338 if (url_response_info_object) { | |
339 googledrivefs->ppapi()->ReleaseResource(url_response_info_object); | |
340 } | |
341 | |
342 return error; | |
343 } | |
344 | |
345 // A Google Drive item is a file or a directory. | |
346 // RequestItemId requests to see if either a file with item_name | |
347 // or a directory with item_name is there. | |
348 // If it is, item_id is set. | |
349 Error RequestItemId(const std::string& parent_dir_id, | |
350 const std::string& item_name, | |
351 Filesystem* filesystem, | |
352 std::string* out_item_id) { | |
353 *out_item_id = ""; | |
354 | |
355 GoogleDriveFs* googledrivefs = static_cast<GoogleDriveFs*>(filesystem); | |
356 | |
357 URLLoaderInterface* url_loader_iface = | |
358 googledrivefs->ppapi()->GetURLLoaderInterface(); | |
359 PP_Resource url_loader_object = | |
360 url_loader_iface->Create(googledrivefs->instance()); | |
361 URLRequestInfoInterface* url_request_info_iface = | |
362 googledrivefs->ppapi()->GetURLRequestInfoInterface(); | |
363 PP_Resource url_request_info_object = | |
364 url_request_info_iface->Create(googledrivefs->instance()); | |
365 PP_Resource url_response_info_object = 0; | |
366 Error error(0); | |
367 std::string output; | |
368 int id_index; | |
369 | |
370 static const char base_url[] = "https://www.googleapis.com/drive/v3/files"; | |
371 | |
372 RequestUrlParams p; | |
373 | |
374 p.url = base_url; | |
375 std::string q_attribute_value = ""; | |
376 std::string attribute_values[] = {"%27" + parent_dir_id + "%27+in+parents", | |
377 "name+=+%27" + item_name + "%27"}; | |
378 AddUrlQAttributeValue(attribute_values, 2, &q_attribute_value); | |
379 AddUrlFirstQueryParameter("q", q_attribute_value, &p.url); | |
380 | |
381 p.method = "GET"; | |
382 | |
383 p.headers = ""; | |
384 | |
385 AddHeaders("Content-type", "application/json", &p.headers); | |
386 AddHeaders("Authorization", "Bearer " + googledrivefs->token(), &p.headers); | |
387 | |
388 error = MakeRequest(googledrivefs->ppapi(), url_loader_object, | |
389 url_request_info_object, p); | |
390 | |
391 if (error) { | |
392 goto done; | |
393 } | |
394 | |
395 error = FinishPreparingResponse(googledrivefs->ppapi(), url_loader_object, | |
396 &url_response_info_object); | |
397 | |
398 if (error) { | |
399 goto done; | |
400 } | |
401 | |
402 if (ReadStatusCode(googledrivefs->ppapi(), url_response_info_object) != | |
403 STATUSCODE_OK) { | |
404 error = EPERM; | |
405 goto done; | |
406 } | |
407 | |
408 error = ReadResponseBody(googledrivefs->ppapi(), googledrivefs->instance(), | |
409 url_response_info_object, INT_MAX, &output); | |
410 | |
411 if (error) { | |
412 goto done; | |
413 } | |
414 | |
415 GetValue(output, "id", 0, out_item_id, &id_index); | |
416 if (id_index == -1) { | |
417 error = ENOENT; | |
418 goto done; | |
419 } | |
420 | |
421 done: | |
422 if (url_loader_object) { | |
423 googledrivefs->ppapi()->ReleaseResource(url_loader_object); | |
424 } | |
425 if (url_request_info_object) { | |
426 googledrivefs->ppapi()->ReleaseResource(url_request_info_object); | |
427 } | |
428 if (url_response_info_object) { | |
429 googledrivefs->ppapi()->ReleaseResource(url_response_info_object); | |
430 } | |
431 | |
432 return error; | |
433 } | |
434 | |
435 Error RequestParentDirId(const Path& path, | |
436 Filesystem* filesystem, | |
437 std::string* out_parent_dir_id) { | |
438 *out_parent_dir_id = ""; | |
439 | |
440 if (path.Size() < 2) { | |
441 return EINVAL; | |
442 } | |
443 | |
444 std::string helper_parent_dir_id = "root"; | |
445 | |
446 for (unsigned int i = 1; i < path.Size() - 1; ++i) { | |
447 std::string dir_name = path.Range(i, i + 1); | |
448 | |
449 std::string dir_id = ""; | |
450 Error error = | |
451 RequestDirId(helper_parent_dir_id, dir_name, filesystem, &dir_id); | |
452 if (error) { | |
453 return error; | |
454 } | |
455 | |
456 helper_parent_dir_id = dir_id; | |
457 } | |
458 *out_parent_dir_id = helper_parent_dir_id; | |
459 | |
460 return 0; | |
461 } | |
462 | |
463 Error GetListFileResponseBody(const std::string& url, | |
binji
2016/08/22 19:21:53
These last few functions look very similar with a
chanpatorikku
2016/08/29 17:14:03
Some combination has been done.
RequestDirent(..)
| |
464 Filesystem* filesystem, | |
465 std::string* out_response_body) { | |
466 GoogleDriveFs* googledrivefs = static_cast<GoogleDriveFs*>(filesystem); | |
467 | |
468 URLLoaderInterface* url_loader_iface = | |
469 googledrivefs->ppapi()->GetURLLoaderInterface(); | |
470 PP_Resource url_loader_object = | |
471 url_loader_iface->Create(googledrivefs->instance()); | |
472 URLRequestInfoInterface* url_request_info_iface = | |
473 googledrivefs->ppapi()->GetURLRequestInfoInterface(); | |
474 PP_Resource url_request_info_object = | |
475 url_request_info_iface->Create(googledrivefs->instance()); | |
476 PP_Resource url_response_info_object = 0; | |
477 Error error(0); | |
478 | |
479 RequestUrlParams p; | |
480 | |
481 p.url = url; | |
482 | |
483 p.method = "GET"; | |
484 | |
485 p.headers = ""; | |
486 | |
487 AddHeaders("Content-type", "application/json", &p.headers); | |
488 AddHeaders("Authorization", "Bearer " + googledrivefs->token(), &p.headers); | |
489 | |
490 error = MakeRequest(googledrivefs->ppapi(), url_loader_object, | |
491 url_request_info_object, p); | |
492 | |
493 if (error) { | |
494 goto done; | |
495 } | |
496 | |
497 error = FinishPreparingResponse(googledrivefs->ppapi(), url_loader_object, | |
498 &url_response_info_object); | |
499 | |
500 if (error) { | |
501 goto done; | |
502 } | |
503 | |
504 if (ReadStatusCode(googledrivefs->ppapi(), url_response_info_object) != | |
505 STATUSCODE_OK) { | |
506 error = EPERM; | |
507 goto done; | |
508 } | |
509 | |
510 error = | |
511 ReadResponseBody(googledrivefs->ppapi(), googledrivefs->instance(), | |
512 url_response_info_object, INT_MAX, out_response_body); | |
513 if (error) { | |
514 goto done; | |
515 } | |
516 | |
517 done: | |
518 if (url_loader_object) { | |
519 googledrivefs->ppapi()->ReleaseResource(url_loader_object); | |
520 } | |
521 if (url_request_info_object) { | |
522 googledrivefs->ppapi()->ReleaseResource(url_request_info_object); | |
523 } | |
524 if (url_response_info_object) { | |
525 googledrivefs->ppapi()->ReleaseResource(url_response_info_object); | |
526 } | |
527 | |
528 return error; | |
529 } | |
530 | |
531 } // namespace nacl_io | |
OLD | NEW |