Merge branch 'tizen_5.0' into tizen_5.5
[platform/core/api/webapi-plugins.git] / src / filesystem / filesystem_instance.cc
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16
17 #include "filesystem/filesystem_instance.h"
18
19 #include <linux/limits.h>
20 #include <cstdint>
21 #include <functional>
22 #include <stdexcept>
23
24 #include <sys/stat.h>
25 #include <system_error>
26 #include "common/logger.h"
27 #include "common/picojson.h"
28 #include "common/platform_exception.h"
29 #include "common/scope_exit.h"
30 #include "common/task-queue.h"
31 #include "common/tools.h"
32 #include "filesystem_manager.h"
33
34 namespace extension {
35 namespace filesystem {
36
37 namespace {
38
39 using common::tools::GetErrorString;
40
41 // The privileges that required in Filesystem API
42 const std::string kPrivilegeFilesystemRead = "http://tizen.org/privilege/filesystem.read";
43 const std::string kPrivilegeFilesystemWrite = "http://tizen.org/privilege/filesystem.write";
44
45 const std::string kISOEncoding = "ISO-8859-1";
46 const std::string kUTF8Encoding = "UTF-8";
47
48 std::string GetFopenMode(const picojson::value& args) {
49   ScopeLogger();
50   const std::string& open_mode = args.get("openMode").get<std::string>();
51
52   if ("rw" == open_mode) {
53     return "r+";
54   } else if ("rwo" == open_mode) {
55     return "w+";
56   }
57
58   return open_mode;
59 }
60
61 bool WriteAccessRequested(const picojson::value& args) {
62   ScopeLogger();
63   const std::string& open_mode = args.get("openMode").get<std::string>();
64
65   return "a" == open_mode || "rw" == open_mode || "rwo" == open_mode || "w" == open_mode;
66 }
67
68 bool ReadAccessRequested(const picojson::value& args) {
69   ScopeLogger();
70   const std::string& open_mode = args.get("openMode").get<std::string>();
71
72   return "r" == open_mode || "rw" == open_mode || "rwo" == open_mode;
73 }
74
75 bool ShouldMakeParents(const picojson::value& args) {
76   ScopeLogger();
77   const std::string& path = args.get("path").get<std::string>();
78   struct stat buf {};
79   if (FilesystemUtils::CheckIfExists(FilesystemUtils::Dirname(path), &buf)) {
80     return false;
81   }
82   bool make_parents = args.get("makeParents").get<bool>();
83   const std::string& open_mode = args.get("openMode").get<std::string>();
84   return make_parents && ("w" == open_mode || "a" == open_mode || "rwo" == open_mode);
85 }
86 }
87
88 using namespace common;
89 using namespace extension::filesystem;
90 using namespace std::string_literals;
91
92 FileHandle::~FileHandle() {
93   ScopeLogger();
94
95   if (file_handle && std::fclose(file_handle)) {
96     LoggerE("close file failed, error message: %s", GetErrorString(errno).c_str());
97   }
98 }
99
100 FilesystemInstance::FilesystemInstance() {
101   ScopeLogger();
102
103   using std::placeholders::_1;
104   using std::placeholders::_2;
105
106 #define REGISTER_METHOD(M) RegisterSyncHandler(#M, std::bind(&FilesystemInstance::M, this, _1, _2))
107
108   REGISTER_METHOD(FileStat);
109   REGISTER_METHOD(FileStatSync);
110   REGISTER_METHOD(FileCreateSync);
111   REGISTER_METHOD(FileReadDir);
112   REGISTER_METHOD(FileRename);
113   REGISTER_METHOD(FileReadBytes);
114   REGISTER_METHOD(FileReadString);
115   REGISTER_METHOD(FileWriteBytes);
116   REGISTER_METHOD(FileWriteBase64);
117   REGISTER_METHOD(FileWriteString);
118   REGISTER_METHOD(FilesystemFetchAllStorages);
119   REGISTER_METHOD(FileSystemManagerAddStorageStateChangeListener);
120   REGISTER_METHOD(FileSystemManagerRemoveStorageStateChangeListener);
121   REGISTER_METHOD(FileSystemManagerFetchStorages);
122   REGISTER_METHOD(FileSystemManagerMakeDirectory);
123   REGISTER_METHOD(FileSystemManagerMakeDirectorySync);
124   REGISTER_METHOD(FileUnlinkFile);
125   REGISTER_METHOD(FileRemoveDirectory);
126   REGISTER_METHOD(FileCopyTo);
127   REGISTER_METHOD(FileSystemManagerGetCanonicalPath);
128
129   REGISTER_METHOD(FileSystemManagerOpenFile);
130   REGISTER_METHOD(FileSystemManagerCreateDirectory);
131   REGISTER_METHOD(FileSystemManagerDeleteFile);
132   REGISTER_METHOD(FileSystemManagerDeleteDirectory);
133   REGISTER_METHOD(FileSystemManagerCopyFile);
134   REGISTER_METHOD(FileSystemManagerCopyDirectory);
135   REGISTER_METHOD(FileSystemManagerMoveFile);
136   REGISTER_METHOD(FileSystemManagerMoveDirectory);
137   REGISTER_METHOD(FileSystemManagerRename);
138   REGISTER_METHOD(FileSystemManagerListDirectory);
139   REGISTER_METHOD(FileSystemManagerIsFile);
140   REGISTER_METHOD(FileSystemManagerIsDirectory);
141   REGISTER_METHOD(FileSystemManagerPathExists);
142   REGISTER_METHOD(FileSystemManagerGetLimits);
143
144   REGISTER_METHOD(FileHandleSeek);
145   REGISTER_METHOD(FileHandleReadString);
146   REGISTER_METHOD(FileHandleWriteString);
147   REGISTER_METHOD(FileHandleReadData);
148   REGISTER_METHOD(FileHandleWriteData);
149   REGISTER_METHOD(FileHandleFlush);
150   REGISTER_METHOD(FileHandleSync);
151   REGISTER_METHOD(FileHandleClose);
152
153 #undef REGISTER_METHOD
154
155   FilesystemManager::GetInstance().AddListener(this);
156 }
157
158 FilesystemInstance::~FilesystemInstance() {
159   ScopeLogger();
160   worker.stop();
161   FilesystemManager::GetInstance().StopListening();
162   FilesystemManager::GetInstance().RemoveListener();
163 }
164
165 #define CHECK_EXIST(args, name, out)                                             \
166   if (!args.contains(name)) {                                                    \
167     LogAndReportError(TypeMismatchException(name " is required argument"), out); \
168     return;                                                                      \
169   }
170
171 void FilesystemInstance::FileCreateSync(const picojson::value& args, picojson::object& out) {
172   ScopeLogger();
173   LoggerW(
174       "DEPRECATION WARNING: File.createFile() is deprecated since Tizen 5.0. Use "
175       "FileSystemManager.openFile() instead.");
176
177   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
178   CHECK_EXIST(args, "location", out)
179   const std::string& location = args.get("location").get<std::string>();
180   CHECK_STORAGE_ACCESS(location, &out);
181
182   auto onSuccess = [&](const FilesystemStat& data) {
183     ScopeLogger("Entered into asynchronous function, onSuccess");
184     ReportSuccess(data.toJSON(), out);
185   };
186
187   auto onError = [&](FilesystemError e) {
188     ScopeLogger("Entered into asynchronous function, onError");
189     PrepareError(e, out);
190   };
191
192   FilesystemManager::GetInstance().CreateFile(location, onSuccess, onError);
193 }
194
195 void FilesystemInstance::FileRename(const picojson::value& args, picojson::object& out) {
196   ScopeLogger();
197   LoggerW(
198       "DEPRECATION WARNING: File.moveTo() is deprecated since Tizen 5.0. Use "
199       "FileSystemManager.moveFile() or FileSystemManager.moveDirectory() instead.");
200
201   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
202   CHECK_EXIST(args, "callbackId", out)
203
204   CHECK_EXIST(args, "oldPath", out)
205   const std::string& oldPath = args.get("oldPath").get<std::string>();
206   CHECK_STORAGE_ACCESS(oldPath, &out);
207   CHECK_EXIST(args, "newPath", out)
208   const std::string& newPath = args.get("newPath").get<std::string>();
209   CHECK_STORAGE_ACCESS(newPath, &out);
210
211   double callback_id = args.get("callbackId").get<double>();
212
213   auto onSuccess = [this, callback_id](const FilesystemStat& data) {
214     ScopeLogger("Entered into asynchronous function, onSuccess");
215     picojson::value response = picojson::value(picojson::object());
216     picojson::object& obj = response.get<picojson::object>();
217     obj["callbackId"] = picojson::value(callback_id);
218     ReportSuccess(data.toJSON(), obj);
219     Instance::PostMessage(this, response.serialize().c_str());
220   };
221
222   auto onError = [this, callback_id](FilesystemError e) {
223     ScopeLogger("Entered into asynchronous function, onError");
224     picojson::value response = picojson::value(picojson::object());
225     picojson::object& obj = response.get<picojson::object>();
226     obj["callbackId"] = picojson::value(callback_id);
227     PrepareError(e, obj);
228     Instance::PostMessage(this, response.serialize().c_str());
229   };
230
231   FilesystemManager& fsm = FilesystemManager::GetInstance();
232   common::TaskQueue::GetInstance().Queue(
233       std::bind(&FilesystemManager::Rename, &fsm, oldPath, newPath, onSuccess, onError));
234 }
235
236 /* Write to str buf bytes as if they were UTF-8 codepoints */
237 static void encode_binary_in_string(const std::vector<std::uint8_t>& buf, std::string& str) {
238   ScopeLogger();
239   str.reserve(str.size() + buf.size());
240
241   for (std::uint8_t byte : buf) {
242     if (byte < 128) {
243       str += byte;
244       continue;
245     }
246     str += 0xC0 | (byte >> 6);
247     str += 0x80 | (byte & 0x3F);
248   }
249 }
250
251 /* Decode (max 2-byte) UTF-8 characters to buf, throws std::runtime_error */
252 static void decode_binary_from_string(const std::string& str, std::vector<std::uint8_t>& buf) {
253   ScopeLogger();
254   buf.reserve(buf.size() + str.size());
255
256   const std::uint8_t* it = (std::uint8_t*)str.data();
257   const std::uint8_t* end = it + str.size();
258   while (it != end) {
259     if (*it < 128) {
260       buf.push_back(*it++);
261       continue;
262     }
263     auto b1 = *it++;
264     if (it == end) {
265       throw std::runtime_error("internal error (invalid UTF-8 sequence)");
266     }
267     auto b2 = *it++;
268     unsigned int x = ((b1 & 0x1F) << 6) | (b2 & 0x3F);
269     buf.push_back(x);
270   }
271 }
272
273 namespace latin1 {
274 static auto to_utf8 = &encode_binary_in_string;
275 /* It does not check if UTF-8 values are representable by ISO-8859-1. Make proper checks and
276  * substitute invalid characters in JavaScript before passing through crosswalk */
277 static auto from_utf8 = &decode_binary_from_string;
278 }
279
280 static constexpr std::size_t NPOS = (std::size_t)(-1);
281
282 /**
283  * On failure throws std::system_error
284  */
285 static std::size_t file_size(FILE* file) {
286   ScopeLogger();
287
288   struct ::stat buf;
289   int status = ::fstat(::fileno(file), &buf);
290   if (status != 0) {
291     throw std::system_error{errno, std::generic_category(), "failed to get file size"};
292   }
293
294   return buf.st_size;
295 }
296
297 /**
298  * Returns the amount of bytes to the EOF starting from current file-position indicator.
299  *
300  * On failure throws std::system_error
301  */
302 static std::size_t file_bytes_to_eof(FILE* file) {
303   ScopeLogger();
304
305   std::size_t total_fize_size = file_size(file);
306   long file_position = ftell(file);
307   if (-1 == file_position) {
308     throw std::system_error{errno, std::generic_category(),
309                             "Failed to get file position"s + GetErrorString(errno)};
310   }
311   return total_fize_size - static_cast<size_t>(file_position);
312 }
313
314 static std::vector<std::uint8_t> read_file(FILE* file, std::size_t length = NPOS,
315                                            std::size_t* read_bytes = nullptr);
316
317 /**
318  * Returns a buffer. If length is NPOS, then it reads whole file, up to the end.
319  * On failure throws std::runtime_error
320  */
321 static std::vector<std::uint8_t> read_file(std::string path, long offset = 0,
322                                            std::size_t length = NPOS) {
323   ScopeLogger();
324
325   FILE* file = std::fopen(path.c_str(), "r");
326   if (!file) {
327     std::string err_msg = std::string("Cannot open file to read. ") + GetErrorString(errno);
328     throw std::system_error{errno, std::generic_category(), err_msg};
329   }
330
331   SCOPE_EXIT {
332     int status = std::fclose(file);
333     if (status) {
334       LoggerE("Cannot close file!");
335     }
336   };
337
338   if (0 != offset && 0 != std::fseek(file, offset, SEEK_SET)) {
339     std::string err_msg = std::string("Cannot perform seek. ") + GetErrorString(errno);
340     throw std::system_error{errno, std::generic_category(), err_msg};
341   }
342
343   if (NPOS == length) {
344     length = file_size(file) - offset;
345   }
346
347   return read_file(file, length);
348 }
349
350 /**
351  * Returns a buffer. If length is NPOS, then it reads whole file, up to the end.
352  * On failure throws std::runtime_error
353  */
354 static std::vector<std::uint8_t> read_file(FILE* file, std::size_t length /*= NPOS*/,
355                                            std::size_t* read_bytes /* = nullptr*/) {
356   ScopeLogger();
357
358   // By default reads whole file. Get the file size.
359   if (NPOS == length) {
360     length = file_size(file);
361   }
362
363   std::vector<std::uint8_t> out_buf;
364   try {
365     out_buf.resize(length);
366   } catch (std::bad_alloc& err) {
367     throw std::runtime_error{"Could not allocate memory"};
368   }
369   std::uint8_t* data_p = out_buf.data();
370   std::uint8_t* end_p = data_p + length;
371   while (data_p != end_p) {
372     data_p += std::fread(data_p, 1, end_p - data_p, file);
373
374     if (std::ferror(file)) {
375       std::string err_msg = std::string("Error during file read. ") + GetErrorString(errno);
376       throw std::runtime_error(err_msg);
377     }
378
379     if (std::feof(file)) {
380       LoggerD("File is at end before buffer is filled. Finish.");
381       break;
382     }
383   }
384   // read_file function is used in API since version 1.0.
385   // read_bytes was added in Tizen 5.0, with default value equal to nullptr, the behaviour is not
386   // changed.
387   // It is used to return the actual number of read bytes, because requested length might be bigger
388   // than possible bytes to be read.
389   if (nullptr != read_bytes) {
390     *read_bytes = std::distance(out_buf.data(), data_p);
391   }
392   return out_buf;
393 }
394
395 /**
396  * On failure throws std::runtime_error
397  */
398 void write_file(const std::uint8_t* data, std::size_t len, FILE* file) {
399   ScopeLogger();
400
401   const std::uint8_t* data_p = data;
402   const std::uint8_t* end_p = data + len;
403   while (data_p != end_p) {
404     data_p += fwrite(data_p, 1, end_p - data_p, file);
405
406     if (std::ferror(file)) {
407       std::string err_msg = std::string("Error during file write. ") + GetErrorString(errno);
408       throw std::runtime_error(err_msg);
409     }
410   }
411
412   if (std::fflush(file)) {
413     std::string err_msg = std::string("Error during file write. ") + GetErrorString(errno);
414     throw std::runtime_error(err_msg);
415   }
416 }
417
418 /**
419  * On failure throws std::runtime_error
420  */
421 void write_file(const std::uint8_t* data, std::size_t len, std::string path, long offset,
422                 const char* mode) {
423   ScopeLogger();
424
425   FILE* file = std::fopen(path.c_str(), mode);
426
427   if (!file) {
428     std::string err_msg = std::string("Cannot open file to write. ") + GetErrorString(errno);
429     throw std::runtime_error(err_msg);
430   }
431
432   SCOPE_EXIT {
433     int status = std::fclose(file);
434     if (status) {
435       LoggerE("Cannot close file!");
436     }
437   };
438
439   if (offset != 0 && std::fseek(file, offset, SEEK_SET) != 0) {
440     std::string err_msg = std::string("Cannot perform seek. ") + GetErrorString(errno);
441     throw std::system_error{errno, std::generic_category(), err_msg};
442   }
443
444   write_file(data, len, file);
445 }
446
447 #define FIRST_BIT_MASK 0x80
448 #define SECOND_BIT_MASK 0x40
449 #define THIRD_BIT_MASK 0x20
450 #define FOURTH_BIT_MASK 0x10
451 #define FIFTH_BIT_MASK 0x08
452
453 enum class ByteOfUTF8Classification {
454   NOT_VALID,
455   ONE_BYTE_CHAR,
456   FIRST_OF_2_BYTES,
457   FIRST_OF_3_BYTES,
458   FIRST_OF_4_BYTES,
459   EXTENSION_BYTE
460 };
461
462 ByteOfUTF8Classification is_utf8_byte(uint8_t byte) {
463   if (FIRST_BIT_MASK & byte) {
464     if (SECOND_BIT_MASK & byte) {
465       if (THIRD_BIT_MASK & byte) {
466         if (FOURTH_BIT_MASK & byte) {
467           if (FIFTH_BIT_MASK & byte) {
468             return ByteOfUTF8Classification::NOT_VALID;
469           } else {
470             return ByteOfUTF8Classification::FIRST_OF_4_BYTES;
471           }
472         } else {
473           return ByteOfUTF8Classification::FIRST_OF_3_BYTES;
474         }
475       } else {
476         return ByteOfUTF8Classification::FIRST_OF_2_BYTES;
477       }
478     } else {
479       return ByteOfUTF8Classification::EXTENSION_BYTE;
480     }
481   } else {
482     return ByteOfUTF8Classification::ONE_BYTE_CHAR;
483   }
484 }
485
486 void skip_partial_character(std::vector<std::uint8_t>& buf) {
487   auto buf_position = buf.begin();
488
489   while (buf.end() != buf_position &&
490          (ByteOfUTF8Classification::EXTENSION_BYTE == is_utf8_byte(*buf_position))) {
491     buf_position = buf.erase(buf_position);
492     LoggerD("Removed UTF-8 Extension Byte from begining of read buffer");
493   }
494 }
495
496 bool validate_and_check_character_count(std::vector<std::uint8_t>& buf, unsigned long& char_count,
497                                         short& no_of_extensions_expected) {
498   ScopeLogger();
499   char_count = 0;
500   no_of_extensions_expected = 0;
501   auto buf_position = buf.begin();
502
503   skip_partial_character(buf);
504
505   while (buf.end() != buf_position) {
506     switch (is_utf8_byte(*buf_position)) {
507       case ByteOfUTF8Classification::EXTENSION_BYTE:
508         no_of_extensions_expected--;
509         if (0 > no_of_extensions_expected) {
510           return false;
511         } else if (0 == no_of_extensions_expected) {
512           char_count++;
513         }
514         break;
515       case ByteOfUTF8Classification::ONE_BYTE_CHAR:
516         if (0 != no_of_extensions_expected) {
517           return false;
518         }
519         char_count++;
520         break;
521       case ByteOfUTF8Classification::FIRST_OF_2_BYTES:
522         if (0 != no_of_extensions_expected) {
523           return false;
524         }
525         no_of_extensions_expected = 1;
526         break;
527       case ByteOfUTF8Classification::FIRST_OF_3_BYTES:
528         if (0 != no_of_extensions_expected) {
529           return false;
530         }
531         no_of_extensions_expected = 2;
532         break;
533       case ByteOfUTF8Classification::FIRST_OF_4_BYTES:
534         if (0 != no_of_extensions_expected) {
535           return false;
536         }
537         no_of_extensions_expected = 3;
538         break;
539       case ByteOfUTF8Classification::NOT_VALID:
540         return false;
541       default:
542         LoggerE("Abnormal value returned from is_utf8_byte function");
543     }
544     buf_position++;
545   }
546   return true;
547 }
548
549 /*
550  * add_utf8_chars_to_buffer
551  * Method returns false if byte read is not a valid utf8 char
552  */
553 bool add_utf8_chars_to_buffer(FILE* file, std::vector<std::uint8_t>& buf, int chars_to_read,
554                               short& extension_bytes_expected) {
555   int character;
556   LoggerD("chars_to_read: %i", chars_to_read);
557   LoggerD("extension_bytes_expected: %i", extension_bytes_expected);
558   while (chars_to_read) {
559     if (extension_bytes_expected) {
560       character = getc(file);
561       if (EOF == character) {
562         return false;
563       }
564       buf.push_back(character);
565       if (!--extension_bytes_expected) {
566         chars_to_read--;
567       }
568       continue;
569     }
570     character = getc(file);
571     if (EOF == character) {
572       return false;
573     }
574     buf.push_back(character);
575     switch (is_utf8_byte(character)) {
576       case ByteOfUTF8Classification::ONE_BYTE_CHAR:
577         chars_to_read--;
578         break;
579       case ByteOfUTF8Classification::FIRST_OF_2_BYTES:
580         extension_bytes_expected = 1;
581         break;
582       case ByteOfUTF8Classification::FIRST_OF_3_BYTES:
583         extension_bytes_expected = 2;
584         break;
585       case ByteOfUTF8Classification::FIRST_OF_4_BYTES:
586         extension_bytes_expected = 3;
587         break;
588       case ByteOfUTF8Classification::EXTENSION_BYTE:
589         LoggerD("unexpected EXTENSION_BYTE");
590         return false;
591       case ByteOfUTF8Classification::NOT_VALID:
592         LoggerE("unexpected NOT_VALID byte while reading utf-8");
593         return false;
594       default:
595         LoggerE("Abnormal. Last read char: %c: ", character);
596         return false;
597     }
598   }
599   return true;
600 }
601
602 namespace base64 {
603 static const char int_to_char[] =
604     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
605 static const std::uint8_t INV = 255;
606 static const std::uint8_t PAD = 254;
607 static const std::uint8_t char_to_int[] = {
608     255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
609     255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
610     255, 255, 255, 255, 255, 62,  255, 255, 255, 63,  52,  53,  54,  55,  56,  57,  58,  59,  60,
611     61,  255, 255, 255, 254, 255, 255, 255, 0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,
612     11,  12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  255, 255, 255, 255,
613     255, 255, 26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,
614     43,  44,  45,  46,  47,  48,  49,  50,  51,  255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
615     255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
616     255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
617     255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
618     255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
619     255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
620     255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
621     255, 255, 255, 255, 255, 255, 255, 255, 255};
622
623 /**
624  * On error throws std::runtime_error
625  */
626 static std::vector<std::uint8_t> decode(const char* str, std::size_t len) {
627   std::vector<std::uint8_t> out;
628   if (len % 4 != 0) throw std::runtime_error("string length is not multiple of 4");
629   if (len == 0) return out;
630
631   const std::uint8_t* p = (std::uint8_t*)str;
632   const std::uint8_t* end = p + len - 4;  // last four can have padding in it
633   std::uint8_t b1, b2, b3, b4;
634   out.reserve(len / 4 * 3);
635   while (p != end) {
636     b1 = char_to_int[*p++];
637     b2 = char_to_int[*p++];
638     b3 = char_to_int[*p++];
639     b4 = char_to_int[*p++];
640
641     if (b1 > 63 || b2 > 63 || b3 > 63 || b4 > 63) throw std::runtime_error("invalid character");
642
643     out.push_back((b1 << 2) | (b2 >> 4));
644     out.push_back((b2 << 4) | (b3 >> 2));
645     out.push_back((b3 << 6) | b4);
646   }
647   b1 = char_to_int[*p++];
648   b2 = char_to_int[*p++];
649   b3 = char_to_int[*p++];
650   b4 = char_to_int[*p++];
651
652   if (b1 == PAD || b1 == INV || b2 == PAD || b2 == INV || b3 == INV || b4 == INV)
653     throw std::runtime_error("invalid character");
654
655   out.push_back((b1 << 2) | (b2 >> 4));
656   if (b3 == PAD) {
657     if (b4 != PAD) throw std::runtime_error("invalid character");
658   } else {
659     out.push_back((b2 << 4) | (b3 >> 2));
660     if (b4 != PAD) out.push_back((b3 << 6) | b4);
661   }
662
663   return out;
664 }
665 }  // namespace base64
666
667 void FilesystemInstance::FileReadString(const picojson::value& args, picojson::object& out) {
668   ScopeLogger();
669   LoggerW(
670       "DEPRECATION WARNING: This method is deprecated since Tizen 5.0.Use FileHandle.readString() "
671       "or FileHandle.readStringNonBlocking() instead.");
672
673   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
674   CHECK_EXIST(args, "location", out)
675   CHECK_EXIST(args, "offset", out)
676
677   const std::string& location = args.get("location").get<std::string>();
678   size_t offset = static_cast<size_t>(args.get("offset").get<double>());
679   size_t length = args.contains("length") ? (size_t)args.get("length").get<double>() : NPOS;
680   const std::string& encoding =
681       args.contains("encoding") ? args.get("encoding").get<std::string>() : "utf-8";
682
683   try {
684     std::vector<std::uint8_t> buf = read_file(location, offset, length);
685
686     if (encoding == "iso-8859-1") {
687       out["result"] = picojson::value(picojson::string_type, true);
688       latin1::to_utf8(buf, out["result"].get<std::string>());
689       ReportSuccess(out);
690     } else {  // default: UTF-8
691       buf.push_back('\0');
692       const char* str = (const char*)buf.data();
693       ReportSuccess(picojson::value{str}, out);
694     }
695   } catch (std::runtime_error& e) {
696     LoggerE("Cannot read file %s, cause: %s", location.c_str(), e.what());
697     PrepareError(FilesystemError::Other, out);
698   }
699 }
700
701 void FilesystemInstance::FileReadBytes(const picojson::value& args, picojson::object& out) {
702   ScopeLogger();
703   LoggerW(
704       "DEPRECATION WARNING: This method is deprecated since Tizen 5.0. Use FileHandle.readData() "
705       "or FileHandle.readDataNonBlocking() instead.");
706
707   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
708   CHECK_EXIST(args, "location", out)
709   CHECK_EXIST(args, "offset", out)
710   CHECK_EXIST(args, "length", out)
711
712   const std::string& location = args.get("location").get<std::string>();
713   size_t offset = static_cast<size_t>(args.get("offset").get<double>());
714   size_t length = static_cast<size_t>(args.get("length").get<double>());
715
716   try {
717     std::vector<std::uint8_t> out_data = read_file(location, offset, length);
718
719     out["result"] = picojson::value(picojson::string_type, true);
720     encode_binary_in_string(out_data, out["result"].get<std::string>());
721     ReportSuccess(out);
722   } catch (std::runtime_error& e) {
723     LoggerE("Cannot read file %s, cause: %s", location.c_str(), e.what());
724     PrepareError(FilesystemError::Other, out);
725   }
726 }
727
728 void FilesystemInstance::FileWriteString(const picojson::value& args, picojson::object& out) {
729   ScopeLogger();
730   LoggerW(
731       "DEPRECATION WARNING: FileStream.write() is deprecated since Tizen 5.0. Use "
732       "FileHandle.writeString() or FileHandle.writeStringNonBlocking() instead.");
733
734   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
735   CHECK_EXIST(args, "location", out)
736   CHECK_EXIST(args, "data", out)
737   CHECK_EXIST(args, "offset", out)
738   CHECK_EXIST(args, "truncate", out)
739
740   const std::string& location = args.get("location").get<std::string>();
741   const std::string& str = args.get("data").get<std::string>();
742   size_t offset = static_cast<size_t>(args.get("offset").get<double>());
743   bool truncate = static_cast<bool>(args.get("truncate").get<bool>());
744   const char* mode = truncate ? "w" : "r+";
745   const std::string& encoding =
746       args.contains("encoding") ? args.get("encoding").get<std::string>() : "utf-8";
747
748   try {
749     if (encoding == "iso-8859-1") {
750       std::vector<std::uint8_t> data;
751       latin1::from_utf8(str, data);
752       write_file(data.data(), data.size(), location, offset, mode);
753       ReportSuccess(picojson::value{(double)data.size()}, out);
754     } else {  // default: UTF-8
755       const std::uint8_t* buf = (const std::uint8_t*)str.c_str();
756       std::size_t len = str.length();
757       write_file(buf, len, location, offset, mode);
758       ReportSuccess(picojson::value{(double)str.size()}, out);
759     }
760   } catch (std::runtime_error& e) {
761     LoggerE("Cannot write to file %s, cause: %s", location.c_str(), e.what());
762     PrepareError(FilesystemError::Other, out);
763     return;
764   }
765 }
766
767 void FilesystemInstance::FileWriteBytes(const picojson::value& args, picojson::object& out) {
768   ScopeLogger();
769   LoggerW(
770       "DEPRECATION WARNING: FileStream.writeBytes() is deprecated since Tizen 5.0. To read and Use "
771       "FileHandle.writeData() or FileHandle.writeDataNonBlocking() instead.");
772
773   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
774   CHECK_EXIST(args, "location", out)
775   CHECK_EXIST(args, "data", out)
776   CHECK_EXIST(args, "offset", out)
777   CHECK_EXIST(args, "truncate", out)
778
779   const std::string& location = args.get("location").get<std::string>();
780   const std::string& str = args.get("data").get<std::string>();
781   size_t offset = static_cast<size_t>(args.get("offset").get<double>());
782   bool truncate = static_cast<bool>(args.get("truncate").get<bool>());
783   const char* mode = truncate ? "w" : "r+";
784
785   try {
786     std::vector<std::uint8_t> data;
787     decode_binary_from_string(str, data);
788     write_file(data.data(), data.size(), location, offset, mode);
789   } catch (std::runtime_error& e) {
790     LoggerE("Cannot write to %s, cause: %s", location.c_str(), e.what());
791     PrepareError(FilesystemError::Other, out);
792     return;
793   }
794
795   ReportSuccess(out);
796 }
797
798 void FilesystemInstance::FileWriteBase64(const picojson::value& args, picojson::object& out) {
799   ScopeLogger();
800   LoggerW(
801       "DEPRECATION WARNING: FileStream.writeBase64() is deprecated since Tizen 5.0. Use "
802       "FileHandle.writeData() or FileHandle.writeDataNonBlocking() in combination with atob() and "
803       "btoa() functions instead.");
804
805   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
806   CHECK_EXIST(args, "location", out)
807   CHECK_EXIST(args, "data", out)
808   CHECK_EXIST(args, "offset", out)
809   CHECK_EXIST(args, "truncate", out)
810
811   const std::string& location = args.get("location").get<std::string>();
812   const std::string& str = args.get("data").get<std::string>();
813   size_t offset = static_cast<size_t>(args.get("offset").get<double>());
814   bool truncate = static_cast<bool>(args.get("truncate").get<bool>());
815   const char* mode = truncate ? "w" : "r+";
816
817   std::vector<std::uint8_t> data;
818   try {
819     data = base64::decode(str.c_str(), str.length());
820   } catch (std::runtime_error& e) {
821     LoggerE("Can't decode base64: %s", e.what());
822     ReportError(InvalidValuesException(std::string("Can't decode base64: ") + e.what()), out);
823     return;
824   }
825
826   try {
827     write_file(data.data(), data.size(), location, offset, mode);
828     ReportSuccess(picojson::value{(double)data.size()}, out);
829   } catch (std::runtime_error& e) {
830     LoggerE("Cannot write to %s, cause: %s", location.c_str(), e.what());
831     PrepareError(FilesystemError::Other, out);
832   }
833 }
834
835 void FilesystemInstance::FileStat(const picojson::value& args, picojson::object& out) {
836   ScopeLogger();
837   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
838   CHECK_EXIST(args, "location", out)
839   const std::string& location = args.get("location").get<std::string>();
840   CHECK_STORAGE_ACCESS(location, &out);
841
842   CHECK_EXIST(args, "callbackId", out)
843   double callback_id = args.get("callbackId").get<double>();
844
845   auto onSuccess = [this, callback_id](const FilesystemStat& data) {
846     ScopeLogger("Entered into asynchronous function, onSuccess");
847     picojson::value response = picojson::value(picojson::object());
848     picojson::object& obj = response.get<picojson::object>();
849     obj["callbackId"] = picojson::value(callback_id);
850     ReportSuccess(data.toJSON(), obj);
851     Instance::PostMessage(this, response.serialize().c_str());
852   };
853
854   auto onError = [this, callback_id](FilesystemError e) {
855     ScopeLogger("Entered into asynchronous function, onError");
856     picojson::value response = picojson::value(picojson::object());
857     picojson::object& obj = response.get<picojson::object>();
858     obj["callbackId"] = picojson::value(callback_id);
859     PrepareError(e, obj);
860     Instance::PostMessage(this, response.serialize().c_str());
861   };
862
863   FilesystemManager& fsm = FilesystemManager::GetInstance();
864   common::TaskQueue::GetInstance().Async(
865       std::bind(&FilesystemManager::StatPath, &fsm, location, onSuccess, onError));
866 }
867
868 void FilesystemInstance::FileStatSync(const picojson::value& args, picojson::object& out) {
869   ScopeLogger();
870   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
871   CHECK_EXIST(args, "location", out)
872   const std::string& location = args.get("location").get<std::string>();
873   CHECK_STORAGE_ACCESS(location, &out);
874
875   auto onSuccess = [&](const FilesystemStat& data) {
876     ScopeLogger("Entered into asynchronous function, onSuccess");
877     ReportSuccess(data.toJSON(), out);
878   };
879
880   auto onError = [&](FilesystemError e) {
881     ScopeLogger("Entered into asynchronous function, onError");
882     PrepareError(e, out);
883   };
884
885   FilesystemManager::GetInstance().StatPath(location, onSuccess, onError);
886 }
887
888 void FilesystemInstance::FilesystemFetchAllStorages(const picojson::value& args,
889                                                     picojson::object& out) {
890   ScopeLogger();
891
892   auto onSuccess = [&](const common::VirtualStorages& result) {
893     ScopeLogger("Entered into asynchronous function, onSuccess");
894     picojson::array roots;
895     for (const auto& root : result) {
896       roots.push_back(root->ToJson());
897     }
898     ReportSuccess(picojson::value(roots), out);
899   };
900
901   auto onError = [&](FilesystemError e) {
902     ScopeLogger("Entered into asynchronous function, onError");
903     PrepareError(e, out);
904   };
905
906   FilesystemManager::GetInstance().FetchAllStorages(onSuccess, onError);
907 }
908
909 void FilesystemInstance::FileSystemManagerFetchStorages(const picojson::value& args,
910                                                         picojson::object& out) {
911   ScopeLogger();
912
913   auto onSuccess = [&](const common::Storages& result) {
914     ScopeLogger("Entered into asynchronous function, onSuccess");
915     picojson::array storages;
916     storages.reserve(result.size());
917     for (const auto storage : result) {
918       storages.push_back(storage->ToJson());
919     }
920     ReportSuccess(picojson::value(storages), out);
921   };
922
923   auto onError = [&](FilesystemError e) {
924     ScopeLogger("Entered into asynchronous function, onError");
925     PrepareError(e, out);
926   };
927
928   FilesystemManager::GetInstance().FetchStorages(onSuccess, onError);
929 }
930 void FilesystemInstance::FileSystemManagerAddStorageStateChangeListener(const picojson::value& args,
931                                                                         picojson::object& out) {
932   ScopeLogger();
933   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
934   FilesystemManager::GetInstance().StartListening();
935   ReportSuccess(out);
936 }
937
938 void FilesystemInstance::FileSystemManagerRemoveStorageStateChangeListener(
939     const picojson::value& args, picojson::object& out) {
940   ScopeLogger();
941   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
942   FilesystemManager::GetInstance().StopListening();
943   ReportSuccess(out);
944 }
945
946 void FilesystemInstance::onFilesystemStateChangeSuccessCallback(const common::Storage& storage) {
947   ScopeLogger();
948
949   picojson::value event = picojson::value(picojson::object());
950   picojson::object& obj = event.get<picojson::object>();
951   obj["label"] = picojson::value(storage.name_);
952   obj["type"] = picojson::value(common::Storage::ToString(storage.type()));
953   obj["state"] = picojson::value(common::Storage::ToString(storage.state()));
954   obj["listenerId"] = picojson::value("StorageStateChangeListener");
955   Instance::PostMessage(this, event.serialize().c_str());
956 }
957
958 void FilesystemInstance::onFilesystemStateChangeErrorCallback() {
959   ScopeLogger();
960   picojson::value event = picojson::value(picojson::object());
961   picojson::object& obj = event.get<picojson::object>();
962   LogAndReportError(UnknownException(std::string("Failed to registerd listener")), obj);
963   obj["listenerId"] = picojson::value("StorageStateChangeListener");
964   LoggerD("Posting: %s", event.serialize().c_str());
965   Instance::PostMessage(this, event.serialize().c_str());
966 }
967
968 void FilesystemInstance::FileSystemManagerMakeDirectory(const picojson::value& args,
969                                                         picojson::object& out) {
970   ScopeLogger();
971   CHECK_EXIST(args, "callbackId", out)
972   CHECK_EXIST(args, "location", out)
973
974   double callback_id = args.get("callbackId").get<double>();
975   const std::string& location = args.get("location").get<std::string>();
976
977   auto onResult = [this, callback_id](FilesystemError e) {
978     ScopeLogger("Entered into asynchronous function, onResult");
979     picojson::value response = picojson::value(picojson::object());
980     picojson::object& obj = response.get<picojson::object>();
981     obj["callbackId"] = picojson::value(callback_id);
982     if (e == FilesystemError::DirectoryExists)
983       ReportSuccess(obj);
984     else
985       PrepareError(e, obj);
986     Instance::PostMessage(this, response.serialize().c_str());
987   };
988
989   auto onAction = [location, onResult]() {
990     ScopeLogger("Entered into asynchronous function, onAction");
991     FilesystemManager::GetInstance().MakeDirectory(location, onResult);
992   };
993
994   common::TaskQueue::GetInstance().Async(onAction);
995 }
996
997 void FilesystemInstance::FileSystemManagerMakeDirectorySync(const picojson::value& args,
998                                                             picojson::object& out) {
999   ScopeLogger();
1000   LoggerW(
1001       "DEPRECATION WARNING: File.createDirectory() is deprecated since Tizen 5.0. Use "
1002       "FileSystemManager.createDirectory() instead.");
1003
1004   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1005   CHECK_EXIST(args, "location", out)
1006   const std::string& location = args.get("location").get<std::string>();
1007   CHECK_STORAGE_ACCESS(location, &out);
1008
1009   auto onResult = [&](FilesystemError e) {
1010     ScopeLogger("Entered into asynchronous function, onResult");
1011     if (e == FilesystemError::DirectoryExists)
1012       ReportSuccess(out);
1013     else
1014       PrepareError(e, out);
1015   };
1016
1017   FilesystemManager::GetInstance().MakeDirectory(location, onResult);
1018 }
1019
1020 void FilesystemInstance::FileReadDir(const picojson::value& args, picojson::object& out) {
1021   ScopeLogger();
1022   LoggerW(
1023       "DEPRECATION WARNING: File.listFiles() is deprecated since Tizen 5.0. Use "
1024       "FileSystemManager.listDirectory() instead.");
1025
1026   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
1027   CHECK_EXIST(args, "pathToDir", out)
1028   CHECK_EXIST(args, "callbackId", out)
1029
1030   double callback_id = args.get("callbackId").get<double>();
1031   const std::string& pathToDir = args.get("pathToDir").get<std::string>();
1032
1033   auto onSuccess = [this, callback_id](const std::vector<std::string>& paths) {
1034     ScopeLogger("Entered into asynchronous function, onSuccess");
1035     picojson::value result = picojson::value(picojson::array());
1036     ;
1037     picojson::array& statPaths = result.get<picojson::array>();
1038     picojson::value response = picojson::value(picojson::object());
1039     picojson::object& obj = response.get<picojson::object>();
1040     obj["callbackId"] = picojson::value(callback_id);
1041     for (auto path : paths) {
1042       FilesystemStat stat = FilesystemStat::getStat(path);
1043
1044       if (FilesystemError::None == stat.error) {
1045         statPaths.push_back(stat.toJSON());
1046       } else {
1047         LoggerW("File stat for path: %s failed with error: %d. Ignoring this entry.", path.c_str(),
1048                 static_cast<std::underlying_type<FilesystemError>::type>(stat.error));
1049       }
1050     }
1051     ReportSuccess(result, obj);
1052     Instance::PostMessage(this, response.serialize().c_str());
1053   };
1054
1055   auto onError = [this, callback_id](FilesystemError e) {
1056     ScopeLogger("Entered into asynchronous function, onError");
1057     picojson::value response = picojson::value(picojson::object());
1058     picojson::object& obj = response.get<picojson::object>();
1059     obj["callbackId"] = picojson::value(callback_id);
1060     PrepareError(e, obj);
1061     Instance::PostMessage(this, response.serialize().c_str());
1062   };
1063
1064   FilesystemManager& fm = FilesystemManager::GetInstance();
1065   common::TaskQueue::GetInstance().Async(
1066       std::bind(&FilesystemManager::ReadDir, &fm, pathToDir, onSuccess, onError));
1067 }
1068
1069 void FilesystemInstance::FileUnlinkFile(const picojson::value& args, picojson::object& out) {
1070   ScopeLogger();
1071   LoggerW(
1072       "DEPRECATION WARNING: File.deleteFile() is deprecated since Tizen 5.0. Use "
1073       "FileSystemManager.deleteFile() instead.");
1074
1075   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1076   CHECK_EXIST(args, "pathToFile", out)
1077   double callback_id = args.get("callbackId").get<double>();
1078   const std::string& pathToFile = args.get("pathToFile").get<std::string>();
1079   CHECK_STORAGE_ACCESS(pathToFile, &out);
1080
1081   auto onSuccess = [this, callback_id]() {
1082     ScopeLogger("Entered into asynchronous function, onSuccess");
1083     picojson::value result = picojson::value();
1084     picojson::value response = picojson::value(picojson::object());
1085     picojson::object& obj = response.get<picojson::object>();
1086     obj["callbackId"] = picojson::value(callback_id);
1087     ReportSuccess(result, obj);
1088     Instance::PostMessage(this, response.serialize().c_str());
1089   };
1090
1091   auto onError = [this, callback_id](FilesystemError e) {
1092     ScopeLogger("Entered into asynchronous function, onError");
1093     picojson::value response = picojson::value(picojson::object());
1094     picojson::object& obj = response.get<picojson::object>();
1095     obj["callbackId"] = picojson::value(callback_id);
1096     PrepareError(e, obj);
1097     Instance::PostMessage(this, response.serialize().c_str());
1098   };
1099
1100   FilesystemManager& fm = FilesystemManager::GetInstance();
1101   common::TaskQueue::GetInstance().Async(
1102       std::bind(&FilesystemManager::UnlinkFile, &fm, pathToFile, onSuccess, onError));
1103 }
1104
1105 void FilesystemInstance::FileRemoveDirectory(const picojson::value& args, picojson::object& out) {
1106   ScopeLogger();
1107   LoggerW(
1108       "DEPRECATION WARNING: File.deleteDirectory() is deprecated since Tizen 5.0. Use "
1109       "FileSystemManager.deleteDirectory() instead.");
1110
1111   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1112   CHECK_EXIST(args, "pathToDelete", out)
1113   double callback_id = args.get("callbackId").get<double>();
1114   const std::string& pathToDelete = args.get("pathToDelete").get<std::string>();
1115   CHECK_STORAGE_ACCESS(pathToDelete, &out);
1116
1117   auto onSuccess = [this, callback_id]() {
1118     ScopeLogger("Entered into asynchronous function, onSuccess");
1119     picojson::value result = picojson::value();
1120     picojson::value response = picojson::value(picojson::object());
1121     picojson::object& obj = response.get<picojson::object>();
1122     obj["callbackId"] = picojson::value(callback_id);
1123     ReportSuccess(result, obj);
1124     Instance::PostMessage(this, response.serialize().c_str());
1125   };
1126
1127   auto onError = [this, callback_id](FilesystemError e) {
1128     ScopeLogger("Entered into asynchronous function, onError");
1129     picojson::value response = picojson::value(picojson::object());
1130     picojson::object& obj = response.get<picojson::object>();
1131     obj["callbackId"] = picojson::value(callback_id);
1132     PrepareError(e, obj);
1133     Instance::PostMessage(this, response.serialize().c_str());
1134   };
1135
1136   FilesystemManager& fm = FilesystemManager::GetInstance();
1137   common::TaskQueue::GetInstance().Queue(
1138       std::bind(&FilesystemManager::RemoveDirectory, &fm, pathToDelete, onSuccess, onError));
1139 }
1140
1141 void FilesystemInstance::FileCopyTo(const picojson::value& args, picojson::object& out) {
1142   ScopeLogger();
1143   LoggerW(
1144       "DEPRECATION WARNING: File.copyTo() is deprecated since Tizen 5.0. Use "
1145       "FileSystemManager.CopyFile() or FileSystemManager.CopyDirectory() instead.");
1146
1147   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1148   CHECK_EXIST(args, "callbackId", out)
1149
1150   CHECK_EXIST(args, "originFilePath", out)
1151   const std::string& originPath = args.get("originFilePath").get<std::string>();
1152   CHECK_STORAGE_ACCESS(originPath, &out);
1153   CHECK_EXIST(args, "destinationFilePath", out)
1154   const std::string& destinationPath = args.get("destinationFilePath").get<std::string>();
1155   CHECK_STORAGE_ACCESS(destinationPath, &out);
1156
1157   CHECK_EXIST(args, "overwrite", out)
1158
1159   double callback_id = args.get("callbackId").get<double>();
1160   const bool& overwrite = args.get("overwrite").get<bool>();
1161
1162   auto onSuccess = [this, callback_id]() {
1163     ScopeLogger("Entered into asynchronous function, onSuccess");
1164     picojson::value result = picojson::value();
1165     picojson::value response = picojson::value(picojson::object());
1166     picojson::object& obj = response.get<picojson::object>();
1167     obj["callbackId"] = picojson::value(callback_id);
1168     ReportSuccess(result, obj);
1169     Instance::PostMessage(this, response.serialize().c_str());
1170   };
1171
1172   auto onError = [this, callback_id](FilesystemError e) {
1173     ScopeLogger("Entered into asynchronous function, onError");
1174     picojson::value response = picojson::value(picojson::object());
1175     picojson::object& obj = response.get<picojson::object>();
1176     obj["callbackId"] = picojson::value(callback_id);
1177     PrepareError(e, obj);
1178     Instance::PostMessage(this, response.serialize().c_str());
1179   };
1180
1181   FilesystemManager& fm = FilesystemManager::GetInstance();
1182   common::TaskQueue::GetInstance().Queue(std::bind(&FilesystemManager::CopyTo, &fm, originPath,
1183                                                    destinationPath, overwrite, onSuccess, onError));
1184 }
1185
1186 void FilesystemInstance::PrepareError(const FilesystemError& error, picojson::object& out) {
1187   ScopeLogger();
1188   switch (error) {
1189     case FilesystemError::None:
1190       LogAndReportError(UnknownException("PLATFORM ERROR"), out);
1191       break;
1192     case FilesystemError::NotFound:
1193       LogAndReportError(NotFoundException("PLATFORM ERROR"), out,
1194                         ("NotFoundException - PLATFORM ERROR"));
1195       break;
1196     case FilesystemError::FileExists:
1197       LogAndReportError(IOException("File already exists"), out,
1198                         ("IOException - File already exists"));
1199       break;
1200     case FilesystemError::DirectoryExists:
1201       LogAndReportError(IOException("Directory already exists"), out,
1202                         ("IOException - Directory already exists"));
1203       break;
1204     case FilesystemError::PermissionDenied:
1205       LogAndReportError(IOException("Permission denied"), out, ("IOException - Permission denied"));
1206       break;
1207     case FilesystemError::IOError:
1208       LogAndReportError(IOException("IO Error"), out, ("IOException - IO Error"));
1209       break;
1210     case FilesystemError::Other:
1211       LogAndReportError(UnknownException("PLATFORM ERROR other"), out,
1212                         ("UnknownException - PLATFORM ERROR other"));
1213       break;
1214     case FilesystemError::InvalidValue:
1215       LogAndReportError(InvalidValuesException("PLATFORM ERROR"), out,
1216                         ("InvalidValuesException - PLATFORM ERROR"));
1217       break;
1218     default:
1219       LogAndReportError(UnknownException("PLATFORM ERROR default"), out,
1220                         ("UnknownException - PLATFORM ERROR default"));
1221       break;
1222   }
1223 }
1224
1225 void FilesystemInstance::FileSystemManagerGetCanonicalPath(const picojson::value& args,
1226                                                            picojson::object& out) {
1227   ScopeLogger();
1228   // TODO: any privilege needed?
1229   CHECK_EXIST(args, "path", out);
1230
1231   const std::string& path = args.get("path").get<std::string>();
1232
1233   auto onSuccess = [&](const std::string& canonicalPath) {
1234     ScopeLogger("Entered into asynchronous function, onSuccess");
1235     ReportSuccess(picojson::value(canonicalPath), out);
1236   };
1237
1238   auto onError = [&](FilesystemError e) {
1239     ScopeLogger("Entered into asynchronous function, onError");
1240     PrepareError(e, out);
1241   };
1242
1243   FilesystemManager::GetInstance().GetCanonicalPath(path, onSuccess, onError);
1244 }
1245
1246 namespace {
1247
1248 FILE* OpenFile(const std::string& path, const std::string& fopen_mode) {
1249   FILE* file = std::fopen(path.c_str(), fopen_mode.c_str());
1250   if (!file) {
1251     throw std::system_error{errno, std::generic_category(), "Could not open file"};
1252   }
1253
1254   return file;
1255 }
1256
1257 FILE* MakeParentsAndOpenFile(const std::string& path, const std::string& fopen_mode) {
1258   /*
1259    * If fopen fails, created parent directories have to be removed.
1260    * Save the path to the first nonexistent parent directory in the file path,
1261    * to know where to start recursive removal
1262    */
1263   std::string first_nonexistent_parent = FilesystemUtils::Dirname(path);
1264   struct ::stat buf;
1265
1266   while (
1267       !FilesystemUtils::CheckIfExists(FilesystemUtils::Dirname(first_nonexistent_parent), &buf)) {
1268     first_nonexistent_parent = FilesystemUtils::Dirname(first_nonexistent_parent);
1269   }
1270
1271   FilesystemUtils::Mkdir(FilesystemUtils::Dirname(path), true);
1272
1273   FILE* file = std::fopen(path.c_str(), fopen_mode.c_str());
1274   if (file) {
1275     return file;
1276   }
1277
1278   std::system_error fopen_error =
1279       std::system_error(errno, std::generic_category(), "Could not open file");
1280
1281   try {
1282     FilesystemUtils::RemoveDirectoryRecursively(first_nonexistent_parent);
1283   } catch (const std::system_error& error) {
1284     LoggerD("Failed to remove created parent directories: %s", error.what());
1285   }
1286
1287   throw fopen_error;
1288 }
1289
1290 }  // namespace
1291
1292 void FilesystemInstance::FileSystemManagerOpenFile(const picojson::value& args,
1293                                                    picojson::object& out) {
1294   ScopeLogger();
1295   const int unique_id = static_cast<int>(args.get("id").get<double>());
1296   if (opened_files.find(unique_id) != opened_files.end()) {
1297     LogAndReportError(IOException("Internal error (id is already in use)"), out);
1298     return;
1299   }
1300
1301   bool access_checked = false;
1302   if (WriteAccessRequested(args)) {
1303     CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1304     access_checked = true;
1305   }
1306
1307   if (ReadAccessRequested(args)) {
1308     CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
1309     access_checked = true;
1310   }
1311
1312   // File open mode received from JS layer can be different than expected by
1313   // WriteAccessRequested and ReadAccessRequested functions. In case like that
1314   // privilege would not be checked and user could gain unauthorized access to file.
1315   // To prevent this situation we only accept specific open modes.
1316   if (false == access_checked) {
1317     const std::string& open_mode = args.get("openMode").get<std::string>();
1318     LogAndReportError(TypeMismatchException("Invalid open mode: " + open_mode), out);
1319     return;
1320   }
1321
1322   const std::string& path = args.get("path").get<std::string>();
1323
1324   CHECK_STORAGE_ACCESS(path, &out);
1325   const std::string open_mode = GetFopenMode(args);
1326   FILE* file = nullptr;
1327   try {
1328     if (ShouldMakeParents(args)) {
1329       file = MakeParentsAndOpenFile(path, open_mode);
1330     } else {
1331       file = OpenFile(path, open_mode);
1332     }
1333   } catch (const std::system_error& error) {
1334     FilesystemUtils::TranslateException(error, out);
1335     return;
1336   }
1337
1338   opened_files.emplace(std::make_pair(unique_id, std::make_shared<FileHandle>(file)));
1339   ReportSuccess(out);
1340 }
1341
1342 void FilesystemInstance::FileSystemManagerCreateDirectory(const picojson::value& args,
1343                                                           picojson::object& out) {
1344   ScopeLogger();
1345   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1346
1347   double callback_id = args.get("callbackId").get<double>();
1348   const std::string& path = args.get("path").get<std::string>();
1349   bool make_parents = args.get("makeParents").get<bool>();
1350   CHECK_STORAGE_ACCESS(path, &out);
1351
1352   common::TaskQueue::GetInstance().Async([this, callback_id, path, make_parents] {
1353     picojson::value response = picojson::value(picojson::object());
1354     picojson::object& obj = response.get<picojson::object>();
1355     obj["callbackId"] = picojson::value(callback_id);
1356
1357     try {
1358       FilesystemUtils::Mkdir(path, make_parents);
1359       ReportSuccess(picojson::value(path), obj);
1360     } catch (const std::system_error& e) {
1361       FilesystemUtils::TranslateException(e, obj);
1362     }
1363     this->PostMessage(response.serialize().c_str());
1364   });
1365
1366   ReportSuccess(out);
1367 }
1368
1369 void FilesystemInstance::FileSystemManagerDeleteFile(const picojson::value& args,
1370                                                      picojson::object& out) {
1371   ScopeLogger();
1372   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1373
1374   double callback_id = args.get("callbackId").get<double>();
1375   const std::string& path = args.get("path").get<std::string>();
1376
1377   CHECK_STORAGE_ACCESS(path, &out);
1378   common::TaskQueue::GetInstance().Async([this, callback_id, path] {
1379     picojson::value response = picojson::value(picojson::object());
1380     picojson::object& obj = response.get<picojson::object>();
1381     obj["callbackId"] = picojson::value(callback_id);
1382     SCOPE_EXIT {
1383       this->PostMessage(response.serialize().c_str());
1384     };
1385
1386     try {
1387       struct stat buf {};
1388       if (!FilesystemUtils::CheckIfExists(path, &buf) || !FilesystemUtils::CheckIfFile(buf)) {
1389         LogAndReportError(NotFoundException("Given path does not point to file."), obj);
1390         return;
1391       }
1392       FilesystemUtils::Unlink(path);
1393       ReportSuccess(picojson::value(FilesystemUtils::Dirname(path)), obj);
1394     } catch (const std::system_error& e) {
1395       FilesystemUtils::TranslateException(e, obj);
1396     }
1397   });
1398
1399   ReportSuccess(out);
1400 }
1401
1402 void FilesystemInstance::FileSystemManagerDeleteDirectory(const picojson::value& args,
1403                                                           picojson::object& out) {
1404   ScopeLogger();
1405   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1406
1407   double callback_id = args.get("callbackId").get<double>();
1408   const std::string& path = args.get("path").get<std::string>();
1409
1410   CHECK_STORAGE_ACCESS(path, &out);
1411   bool recursive = args.get("recursive").get<bool>();
1412
1413   common::TaskQueue::GetInstance().Queue([this, callback_id, path, recursive] {
1414     ScopeLogger();
1415     picojson::value response = picojson::value(picojson::object());
1416     picojson::object& obj = response.get<picojson::object>();
1417     obj["callbackId"] = picojson::value(callback_id);
1418     SCOPE_EXIT {
1419       this->PostMessage(response.serialize().c_str());
1420     };
1421
1422     try {
1423       struct stat buf {};
1424       if (!FilesystemUtils::CheckIfExists(path, &buf) || !FilesystemUtils::CheckIfDir(buf)) {
1425         LogAndReportError(NotFoundException("Given path does not point to directory."), obj);
1426         return;
1427       }
1428       if (recursive) {
1429         FilesystemUtils::RemoveDirectoryRecursively(path);
1430       } else {
1431         FilesystemUtils::RemoveDirectory(path);
1432       }
1433       ReportSuccess(picojson::value(FilesystemUtils::Dirname(path)), obj);
1434     } catch (const std::system_error& e) {
1435       FilesystemUtils::TranslateException(e, obj);
1436     }
1437   });
1438
1439   ReportSuccess(out);
1440 }
1441
1442 void FilesystemInstance::FileSystemManagerCopyFile(const picojson::value& args,
1443                                                    picojson::object& out) {
1444   ScopeLogger();
1445   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1446   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
1447
1448   double callback_id = args.get("callbackId").get<double>();
1449   const std::string& path = args.get("path").get<std::string>();
1450   CHECK_STORAGE_ACCESS(path, &out);
1451   const std::string& destination_path = args.get("destinationPath").get<std::string>();
1452   CHECK_STORAGE_ACCESS(destination_path, &out);
1453   bool overwrite = args.get("overwrite").get<bool>();
1454
1455   common::TaskQueue::GetInstance().Queue([this, callback_id, path, destination_path, overwrite] {
1456     picojson::value response = picojson::value(picojson::object());
1457     picojson::object& obj = response.get<picojson::object>();
1458     obj["callbackId"] = picojson::value(callback_id);
1459     SCOPE_EXIT {
1460       this->PostMessage(response.serialize().c_str());
1461     };
1462
1463     try {
1464       struct stat buf {};
1465       if (!FilesystemUtils::CheckIfExists(path, &buf) || !FilesystemUtils::CheckIfFile(buf)) {
1466         LogAndReportError(NotFoundException("Given path does not point to file."), obj);
1467         return;
1468       }
1469       buf = {};
1470       if (FilesystemUtils::CheckIfExists(destination_path, &buf) && !overwrite) {
1471         LogAndReportError(
1472             IOException("Given path points to an existing resource, overwriting is not allowed."),
1473             obj);
1474         return;
1475       }
1476       FilesystemUtils::CopyFile(path, destination_path, overwrite);
1477       ReportSuccess(picojson::value(destination_path), obj);
1478     } catch (const std::system_error& e) {
1479       FilesystemUtils::TranslateException(e, obj);
1480     }
1481   });
1482
1483   ReportSuccess(out);
1484 }
1485
1486 void FilesystemInstance::FileSystemManagerCopyDirectory(const picojson::value& args,
1487                                                         picojson::object& out) {
1488   ScopeLogger();
1489   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
1490   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1491   const std::string& path = args.get("path").get<std::string>();
1492   CHECK_STORAGE_ACCESS(path, &out);
1493   const std::string& destination_path = args.get("destinationPath").get<std::string>();
1494   CHECK_STORAGE_ACCESS(destination_path, &out);
1495   double callback_id = args.get("callbackId").get<double>();
1496   bool overwrite = args.get("overwrite").get<bool>();
1497
1498   common::TaskQueue::GetInstance().Queue([this, callback_id, path, destination_path, overwrite] {
1499     ScopeLogger();
1500     picojson::value response = picojson::value(picojson::object());
1501     picojson::object& obj = response.get<picojson::object>();
1502     obj["callbackId"] = picojson::value(callback_id);
1503     SCOPE_EXIT {
1504       this->PostMessage(response.serialize().c_str());
1505     };
1506
1507     try {
1508       struct stat buf {};
1509       if (!FilesystemUtils::CheckIfExists(path, &buf) || !FilesystemUtils::CheckIfDir(buf)) {
1510         LogAndReportError(NotFoundException("Given path does not point to directory."), obj);
1511         return;
1512       }
1513       buf = {};
1514       bool exists = FilesystemUtils::CheckIfExists(destination_path, &buf);
1515       if (exists && !FilesystemUtils::CheckIfDir(buf)) {
1516         LogAndReportError(IOException("File with conflicting name already exists."), obj);
1517         return;
1518       } else if (!exists) {
1519         FilesystemUtils::Mkdir(destination_path);
1520       }
1521       FilesystemUtils::CopyDirectory(path, destination_path, overwrite);
1522       ReportSuccess(picojson::value(destination_path), obj);
1523     } catch (const std::system_error& e) {
1524       FilesystemUtils::TranslateException(e, obj);
1525     }
1526   });
1527
1528   ReportSuccess(out);
1529 }
1530
1531 void FilesystemInstance::FileSystemManagerMoveFile(const picojson::value& args,
1532                                                    picojson::object& out) {
1533   ScopeLogger();
1534   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
1535   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1536
1537   const std::string& path = args.get("path").get<std::string>();
1538   CHECK_STORAGE_ACCESS(path, &out);
1539   const std::string& destination_path = args.get("destinationPath").get<std::string>();
1540   CHECK_STORAGE_ACCESS(destination_path, &out);
1541   double callback_id = args.get("callbackId").get<double>();
1542   bool overwrite = args.get("overwrite").get<bool>();
1543
1544   common::TaskQueue::GetInstance().Queue([this, callback_id, path, destination_path, overwrite] {
1545     ScopeLogger();
1546     picojson::value response = picojson::value(picojson::object());
1547     picojson::object& obj = response.get<picojson::object>();
1548     obj["callbackId"] = picojson::value(callback_id);
1549     SCOPE_EXIT {
1550       this->PostMessage(response.serialize().c_str());
1551     };
1552
1553     try {
1554       struct stat buf {};
1555       if (!FilesystemUtils::CheckIfExists(path, &buf) || !FilesystemUtils::CheckIfFile(buf)) {
1556         LogAndReportError(NotFoundException("Given path does not point to file."), obj);
1557         return;
1558       }
1559       buf = {};
1560       if (!FilesystemUtils::CheckIfExists(destination_path, &buf) ||
1561           !FilesystemUtils::CheckIfDir(buf)) {
1562         LogAndReportError(NotFoundException("Given path does not point to directory."), obj);
1563         return;
1564       }
1565
1566       buf = {};
1567       std::string new_path = destination_path + '/' + FilesystemUtils::PosixBasename(path);
1568       if (!overwrite && FilesystemUtils::CheckIfExists(new_path, &buf)) {
1569         LogAndReportError(IOException("File or directory with conflicting name already exists."),
1570                           obj);
1571         return;
1572       }
1573       FilesystemUtils::MoveFile(path, new_path, overwrite);
1574       ReportSuccess(picojson::value(new_path), obj);
1575     } catch (const std::system_error& e) {
1576       FilesystemUtils::TranslateException(e, obj);
1577     }
1578   });
1579
1580   ReportSuccess(out);
1581 }
1582
1583 void FilesystemInstance::FileSystemManagerMoveDirectory(const picojson::value& args,
1584                                                         picojson::object& out) {
1585   ScopeLogger();
1586
1587   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
1588   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1589   double callback_id = args.get("callbackId").get<double>();
1590   const std::string& path = args.get("path").get<std::string>();
1591   CHECK_STORAGE_ACCESS(path, &out);
1592   const std::string& destination_path = args.get("destinationPath").get<std::string>();
1593   CHECK_STORAGE_ACCESS(destination_path, &out);
1594   bool overwrite = args.get("overwrite").get<bool>();
1595
1596   common::TaskQueue::GetInstance().Queue([this, callback_id, path, destination_path, overwrite] {
1597     picojson::value response = picojson::value(picojson::object());
1598     picojson::object& obj = response.get<picojson::object>();
1599     obj["callbackId"] = picojson::value(callback_id);
1600     SCOPE_EXIT {
1601       this->PostMessage(response.serialize().c_str());
1602     };
1603
1604     try {
1605       struct stat buf {};
1606       if (!FilesystemUtils::CheckIfExists(path, &buf) || !FilesystemUtils::CheckIfDir(buf)) {
1607         LogAndReportError(NotFoundException("Given path does not point to directory."), obj);
1608         return;
1609       }
1610       buf = {};
1611       if (!FilesystemUtils::CheckIfExists(destination_path, &buf) ||
1612           !FilesystemUtils::CheckIfDir(buf)) {
1613         LogAndReportError(NotFoundException("Given path does not point to directory."), obj);
1614         return;
1615       }
1616       buf = {};
1617       std::string new_path = destination_path + '/' + FilesystemUtils::PosixBasename(path);
1618       if (FilesystemUtils::CheckIfExists(new_path, &buf) && !FilesystemUtils::CheckIfDir(buf)) {
1619         LogAndReportError(IOException("File or directory with conflicting name already exists."),
1620                           obj);
1621         return;
1622       }
1623       FilesystemUtils::MoveDirectory(path, destination_path, overwrite);
1624       ReportSuccess(picojson::value(new_path), obj);
1625     } catch (const std::system_error& e) {
1626       FilesystemUtils::TranslateException(e, obj);
1627     }
1628   });
1629
1630   ReportSuccess(out);
1631 }
1632
1633 void FilesystemInstance::FileSystemManagerRename(const picojson::value& args,
1634                                                  picojson::object& out) {
1635   ScopeLogger();
1636   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out);
1637   const std::string& path = args.get("path").get<std::string>();
1638
1639   CHECK_STORAGE_ACCESS(path, &out);
1640   double callback_id = args.get("callbackId").get<double>();
1641   const std::string& new_name = args.get("newName").get<std::string>();
1642
1643   common::TaskQueue::GetInstance().Async([this, callback_id, new_name, path] {
1644     ScopeLogger();
1645     picojson::value response = picojson::value(picojson::object());
1646     picojson::object& obj = response.get<picojson::object>();
1647     obj["callbackId"] = picojson::value(callback_id);
1648     SCOPE_EXIT {
1649       this->PostMessage(response.serialize().c_str());
1650     };
1651
1652     try {
1653       struct stat buf {};
1654       bool exists = FilesystemUtils::CheckIfExists(path, &buf);
1655       if (!exists) {
1656         LogAndReportError(NotFoundException("Given path does not point to file or directory."),
1657                           obj);
1658         return;
1659       }
1660       std::string new_path{FilesystemUtils::Dirname(path) + "/" + new_name};
1661       buf = {};
1662       exists = FilesystemUtils::CheckIfExists(new_path, &buf);
1663       if (exists) {
1664         LogAndReportError(IOException("File or directory with conflicting name already exists"),
1665                           obj);
1666         return;
1667       }
1668       FilesystemUtils::Rename(path, new_path);
1669       ReportSuccess(picojson::value(new_path), obj);
1670     } catch (const std::system_error& e) {
1671       FilesystemUtils::TranslateException(e, obj);
1672     }
1673   });
1674
1675   ReportSuccess(out);
1676 }
1677
1678 void FilterResult(std::vector<const char*>& names, std::vector<unsigned char>& types, bool is_type,
1679                   unsigned char type) {
1680   int i = (int)names.size() - 1;
1681
1682   while (i >= 0) {
1683     if (is_type ? type != types[i] : type == types[i]) {
1684       names.erase(names.begin() + i);
1685     }
1686     i--;
1687   }
1688 }
1689
1690 void FilesystemInstance::FileSystemManagerListDirectory(const picojson::value& args,
1691                                                         picojson::object& out) {
1692   ScopeLogger();
1693   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
1694
1695   double callback_id = args.get("callbackId").get<double>();
1696   const std::string& path = args.get("path").get<std::string>();
1697   const picojson::object& filter = args.get("filter").get<picojson::object>();
1698   CHECK_STORAGE_ACCESS(path, &out);
1699
1700   common::TaskQueue::GetInstance().Async([this, callback_id, path, filter] {
1701     ScopeLogger();
1702     picojson::value response = picojson::value(picojson::object());
1703     picojson::object& obj = response.get<picojson::object>();
1704     obj["callbackId"] = picojson::value(callback_id);
1705
1706     try {
1707       std::vector<const char*> names;
1708       {
1709         std::vector<unsigned char> types;
1710         FilesystemUtils::ListDirectory(path, [&](const char* name, unsigned char type) {
1711           names.push_back(name);
1712           types.push_back(type);
1713         });
1714
1715         auto it = filter.find("isFile");
1716         if (filter.end() != it) {
1717           FilterResult(names, types, it->second.get<bool>(), DT_REG);
1718         }
1719
1720         it = filter.find("isDirectory");
1721         if (filter.end() != it) {
1722           FilterResult(names, types, it->second.get<bool>(), DT_DIR);
1723         }
1724       }
1725
1726       auto start_modified_it = filter.find("startModified"),
1727            end_modified_it = filter.find("endModified"),
1728            start_created_it = filter.find("startCreated"),
1729            end_created_it = filter.find("endCreated");
1730       if (filter.end() != start_modified_it || filter.end() != end_modified_it ||
1731           filter.end() != start_created_it || filter.end() != end_created_it) {
1732         auto name_iterator = names.begin();
1733         while (name_iterator != names.end()) {
1734           struct ::stat buf;
1735           std::string path_with_name = path + std::string("/") + std::string(*name_iterator);
1736           int status = ::stat(path_with_name.c_str(), &buf);
1737           if (status != 0) {
1738             throw std::system_error{errno, std::generic_category(),
1739                                     "Failed to get last modification date of a file"};
1740           }
1741           if (filter.end() != start_modified_it &&
1742               (buf.st_mtime < start_modified_it->second.get<double>())) {
1743             name_iterator = names.erase(name_iterator);
1744             continue;
1745           }
1746           if (filter.end() != end_modified_it &&
1747               (buf.st_mtime > end_modified_it->second.get<double>())) {
1748             name_iterator = names.erase(name_iterator);
1749             continue;
1750           }
1751           if (filter.end() != start_created_it &&
1752               (buf.st_ctime < start_created_it->second.get<double>())) {
1753             name_iterator = names.erase(name_iterator);
1754             continue;
1755           }
1756           if (filter.end() != end_created_it &&
1757               (buf.st_ctime > end_created_it->second.get<double>())) {
1758             name_iterator = names.erase(name_iterator);
1759             continue;
1760           }
1761           name_iterator++;
1762         }
1763       }
1764
1765       picojson::value value = picojson::value(picojson::object());
1766       picojson::object& object = value.get<picojson::object>();
1767
1768       object["names"] = picojson::value{picojson::array_type, true};
1769       object["path"] = picojson::value(path);
1770
1771       picojson::array& names_array = object["names"].get<picojson::array>();
1772       names_array.reserve(names.size());
1773       for (unsigned int i = 0; i < names.size(); ++i) {
1774         names_array.push_back(picojson::value(names[i]));
1775       }
1776
1777       ReportSuccess(value, obj);
1778     } catch (const std::system_error& e) {
1779       FilesystemUtils::TranslateException(e, obj);
1780     }
1781     this->PostMessage(response.serialize().c_str());
1782   });
1783
1784   ReportSuccess(out);
1785 }
1786
1787 void FilesystemInstance::FileSystemManagerIsFile(const picojson::value& args,
1788                                                  picojson::object& out) {
1789   ScopeLogger();
1790   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
1791   const std::string& path = args.get("path").get<std::string>();
1792
1793   CHECK_STORAGE_ACCESS(path, &out);
1794   picojson::value is_file{};
1795   try {
1796     struct stat buf {};
1797     bool exists = FilesystemUtils::CheckIfExists(path, &buf);
1798     if (!exists) {
1799       LogAndReportError(NotFoundException("Given path does not point to file."), out);
1800       return;
1801     }
1802     is_file = picojson::value{FilesystemUtils::CheckIfFile(buf)};
1803   } catch (const std::system_error& e) {
1804     FilesystemUtils::TranslateException(e, out);
1805   }
1806   ReportSuccess(is_file, out);
1807 }
1808
1809 void FilesystemInstance::FileSystemManagerIsDirectory(const picojson::value& args,
1810                                                       picojson::object& out) {
1811   ScopeLogger();
1812   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
1813   const std::string& path = args.get("path").get<std::string>();
1814
1815   CHECK_STORAGE_ACCESS(path, &out);
1816   picojson::value is_directory{};
1817   try {
1818     struct stat buf {};
1819     bool exists = FilesystemUtils::CheckIfExists(path, &buf);
1820     if (!exists) {
1821       LogAndReportError(NotFoundException("Given path does not point to directory."), out);
1822       return;
1823     }
1824     is_directory = picojson::value{FilesystemUtils::CheckIfDir(buf)};
1825   } catch (const std::system_error& e) {
1826     FilesystemUtils::TranslateException(e, out);
1827   }
1828   ReportSuccess(is_directory, out);
1829 }
1830
1831 void FilesystemInstance::FileSystemManagerPathExists(const picojson::value& args,
1832                                                      picojson::object& out) {
1833   ScopeLogger();
1834   CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemRead, &out);
1835   const std::string& path = args.get("path").get<std::string>();
1836
1837   CHECK_STORAGE_ACCESS(path, &out);
1838   picojson::value does_file_exist = picojson::value{true};
1839   try {
1840     struct stat buf {};
1841     bool exists = FilesystemUtils::CheckIfExists(path, &buf);
1842     if (!exists) {
1843       does_file_exist = picojson::value{false};
1844     }
1845   } catch (const std::system_error& e) {
1846     FilesystemUtils::TranslateException(e, out);
1847   }
1848   ReportSuccess(does_file_exist, out);
1849 }
1850
1851 void FilesystemInstance::FileSystemManagerGetLimits(const picojson::value& args,
1852                                                     picojson::object& out) {
1853   ScopeLogger();
1854   picojson::value response =
1855       picojson::value{picojson::array{picojson::value{static_cast<double>(NAME_MAX)},
1856                                       picojson::value{static_cast<double>(PATH_MAX)}}};
1857   ReportSuccess(response, out);
1858 }
1859
1860 void FilesystemInstance::FileHandleSeek(const picojson::value& args, picojson::object& out) {
1861   ScopeLogger();
1862   const int fh_id = static_cast<int>(args.get("id").get<double>());
1863   const long offset = static_cast<long>(args.get("offset").get<double>());
1864   const std::string& _whence = args.get("whence").get<std::string>();
1865
1866   auto fh = opened_files.find(fh_id);
1867
1868   int whence = SEEK_SET;
1869
1870   if ("CURRENT" == _whence) {
1871     whence = SEEK_CUR;
1872     LoggerD("SEEK_CUR selected");
1873   } else if ("END" == _whence) {
1874     whence = SEEK_END;
1875     LoggerD("SEEK_END selected");
1876   }
1877
1878   if (opened_files.end() == fh) {
1879     LogAndReportError(IOException("Invalid FileHandle"), out);
1880     return;
1881   }
1882
1883   auto handle = fh->second;
1884
1885   auto logic = [handle, whence, offset](decltype(out) out) {
1886     long ret = fseek(handle->file_handle, offset, whence);
1887     if (0 != ret) {
1888       LoggerE("fseek returned failed");
1889       std::string error_message =
1890           std::string("seek failed, fileHandle may be corrupted, error message: ") +
1891           GetErrorString(errno);
1892       LogAndReportError(IOException(error_message.c_str()), out);
1893       return;
1894     }
1895
1896     ret = ftell(handle->file_handle);
1897     if (-1L == ret) {
1898       LoggerE("ftell returned failed");
1899       std::string error_message =
1900           std::string("seek failed, fileHandle may be corrupted, error message: ") +
1901           GetErrorString(errno);
1902       LogAndReportError(IOException(error_message.c_str()), out);
1903       return;
1904     }
1905
1906     ReportSuccess(picojson::value((double)ret), out);
1907   };
1908
1909   bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
1910
1911   if (blocking) {
1912     logic(out);
1913   } else {
1914     // Async logic
1915     double callback_id = args.get("callbackId").get<double>();
1916     this->worker.add_job([this, callback_id, logic] {
1917       picojson::value response = picojson::value(picojson::object());
1918       picojson::object& async_out = response.get<picojson::object>();
1919       async_out["callbackId"] = picojson::value(callback_id);
1920       logic(async_out);
1921       this->PostMessage(response.serialize().c_str());
1922     });
1923
1924     // Sync return
1925     ReportSuccess(out);
1926   }
1927 }
1928
1929 void FilesystemInstance::FileHandleReadString(const picojson::value& args, picojson::object& out) {
1930   ScopeLogger();
1931   CHECK_EXIST(args, "id", out)
1932   const int fh_id = static_cast<int>(args.get("id").get<double>());
1933   const std::string& encoding =
1934       args.contains("encoding") ? args.get("encoding").get<std::string>() : "UTF-8";
1935   if (encoding != kISOEncoding && encoding != kUTF8Encoding) {
1936     LogAndReportError(NotSupportedException("Given encoding is not supported."), out);
1937     return;
1938   }
1939
1940   auto fh = opened_files.find(fh_id);
1941   if (opened_files.end() == fh) {
1942     LogAndReportError(IOException("Invalid FileHandle"), out);
1943     return;
1944   }
1945
1946   size_t count;
1947   bool whole_file = false;
1948   if (args.contains("count")) {
1949     // If user passed 'count' parameter, we need to read at most 'count' characters.
1950     double count_double = args.get("count").get<double>();
1951     if (std::string::npos <= static_cast<unsigned long long>(count_double)) {
1952       LogAndReportError(InvalidValuesException("Invalid count was given"), out);
1953       return;
1954     }
1955     count = static_cast<size_t>(count_double);
1956   } else {
1957     try {
1958       count = file_size(fh->second->file_handle);
1959       whole_file = true;
1960     } catch (const std::system_error& e) {
1961       LogAndReportError(IOException(e.what()), out);
1962       return;
1963     }
1964   }
1965   LoggerD("count: %zu", count);
1966
1967   auto handle = fh->second;
1968
1969   auto logic = [handle, count, encoding, whole_file](decltype(out) out) {
1970     try {
1971       size_t read_bytes = 0;
1972       std::vector<std::uint8_t> buf = read_file(handle->file_handle, count, &read_bytes);
1973       buf.resize(read_bytes);          // this protects from reporting too big arrays to JS
1974       if (encoding == kISOEncoding) {  // for iso-8859-1 1 byte is equal to 1 character
1975         out["result"] = picojson::value(picojson::string_type, true);
1976         latin1::to_utf8(buf, out["result"].get<std::string>());
1977         ReportSuccess(out);
1978       } else {  // UTF-8
1979         unsigned long char_count;
1980         short expected_extension_bytes;
1981         if (!validate_and_check_character_count(buf, char_count, expected_extension_bytes)) {
1982           LogAndReportError(
1983               IOException("File doesn't contain UTF-8 encoded string with given length"), out);
1984           return;
1985         }
1986         LoggerD("char_count: %lu", char_count);
1987         LoggerD("ftell: %ld", ftell(handle->file_handle));
1988         if (!(std::feof(
1989                 handle->file_handle))) {  // read number of characters if not whole file read
1990           LoggerD("count parameter given: %zu", count);
1991           if (!whole_file &&
1992               !add_utf8_chars_to_buffer(handle->file_handle, buf, count - char_count,
1993                                         expected_extension_bytes)) {
1994             LogAndReportError(
1995                 IOException("File doesn't contain UTF-8 encoded string with given length"), out);
1996           }
1997         }
1998         const char* str = (const char*)buf.data();
1999         ReportSuccess(picojson::value{str, buf.size()}, out);
2000       }
2001     } catch (std::runtime_error& e) {
2002       LoggerE("Cannot read, cause: %s", e.what());
2003       LogAndReportError(IOException(e.what()), out);
2004     }
2005   };
2006
2007   bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
2008
2009   if (blocking) {
2010     logic(out);
2011   } else {
2012     // Async logic
2013     double callback_id = args.get("callbackId").get<double>();
2014     this->worker.add_job([this, callback_id, logic] {
2015       picojson::value response = picojson::value(picojson::object());
2016       picojson::object& async_out = response.get<picojson::object>();
2017       async_out["callbackId"] = picojson::value(callback_id);
2018       logic(async_out);
2019       this->PostMessage(response.serialize().c_str());
2020     });
2021
2022     // Sync return
2023     ReportSuccess(out);
2024   }
2025 }
2026
2027 void FilesystemInstance::FileHandleWriteString(const picojson::value& args, picojson::object& out) {
2028   ScopeLogger();
2029   CHECK_EXIST(args, "id", out)
2030   CHECK_EXIST(args, "string", out)
2031   const int fh_id = static_cast<int>(args.get("id").get<double>());
2032   const std::string& str = args.get("string").get<std::string>();
2033   const std::string& encoding =
2034       args.contains("encoding") ? args.get("encoding").get<std::string>() : "UTF-8";
2035   if (encoding != kISOEncoding && encoding != kUTF8Encoding) {
2036     LogAndReportError(NotSupportedException("Given encoding is not supported."), out);
2037     return;
2038   }
2039
2040   auto fh = opened_files.find(fh_id);
2041   if (opened_files.end() == fh) {
2042     LogAndReportError(IOException("Invalid FileHandle"), out);
2043     return;
2044   }
2045
2046   auto handle = fh->second;
2047
2048   auto logic = [str, handle, encoding](decltype(out) out) {
2049     try {
2050       std::vector<std::uint8_t> data;
2051       data.resize(str.size());
2052
2053       if (encoding == kISOEncoding) {
2054         latin1::from_utf8(str, data);
2055       } else {  // UTF-8
2056         LoggerD("copying string memory to vector");
2057         std::memcpy(data.data(), str.data(), str.size());
2058       }
2059       write_file(data.data(), data.size(), handle->file_handle);
2060       ReportSuccess(picojson::value{(double)data.size()}, out);
2061     } catch (std::runtime_error& e) {
2062       LoggerE("Cannot write, cause: %s", e.what());
2063       LogAndReportError(IOException(e.what()), out);
2064     }
2065   };
2066
2067   bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
2068
2069   if (blocking) {
2070     logic(out);
2071   } else {
2072     // Async logic
2073     double callback_id = args.get("callbackId").get<double>();
2074     this->worker.add_job([this, callback_id, logic] {
2075       picojson::value response = picojson::value(picojson::object());
2076       picojson::object& async_out = response.get<picojson::object>();
2077       async_out["callbackId"] = picojson::value(callback_id);
2078       logic(async_out);
2079       this->PostMessage(response.serialize().c_str());
2080     });
2081
2082     // Sync return
2083     ReportSuccess(out);
2084   }
2085 }
2086
2087 void FilesystemInstance::FileHandleReadData(const picojson::value& args, picojson::object& out) {
2088   ScopeLogger();
2089   CHECK_EXIST(args, "id", out)
2090   const int fh_id = static_cast<int>(args.get("id").get<double>());
2091   auto fh = opened_files.find(fh_id);
2092
2093   if (opened_files.end() == fh) {
2094     LoggerE("FileHandle with id: %d not found", fh_id);
2095     LogAndReportError(IOException("Invalid FileHandle"), out);
2096     return;
2097   }
2098
2099   size_t size;
2100   // We need to check how many bytes is it possible to read until the EOF.
2101   try {
2102     // We need to read from file exactly the minimum value of 'size' given by user and the
2103     // 'size ' to avoid returning array with redundant data (which would be equal to 0).
2104     size = file_bytes_to_eof(fh->second->file_handle);
2105     if (args.contains("size")) {
2106       // If user passed 'size' parameter, we need to read at most 'size' bytes.
2107       double size_double = args.get("size").get<double>();
2108       if (std::string::npos <= static_cast<unsigned long long>(size_double)) {
2109         LogAndReportError(InvalidValuesException("Invalid size was given"), out);
2110         return;
2111       }
2112       size = std::min(static_cast<size_t>(size_double), size);
2113     }
2114   } catch (const std::system_error& e) {
2115     LogAndReportError(IOException(e.what()), out);
2116     return;
2117   }
2118
2119   LoggerD("size: %zu", size);
2120
2121   auto handle = fh->second;
2122
2123   auto logic = [handle, size](decltype(out) out) {
2124     try {
2125       std::vector<std::uint8_t> data = read_file(handle->file_handle, size);
2126       out["result"] = picojson::value(picojson::string_type, true);
2127       encode_binary_in_string(data, out["result"].get<std::string>());
2128     } catch (std::runtime_error& e) {
2129       LoggerE("Cannot read, cause: %s", e.what());
2130       LogAndReportError(IOException(e.what()), out);
2131     }
2132   };
2133
2134   bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
2135
2136   if (blocking) {
2137     logic(out);
2138   } else {
2139     // Async logic
2140     double callback_id = args.get("callbackId").get<double>();
2141     this->worker.add_job([this, callback_id, logic] {
2142       picojson::value response = picojson::value(picojson::object());
2143       picojson::object& async_out = response.get<picojson::object>();
2144       async_out["callbackId"] = picojson::value(callback_id);
2145       logic(async_out);
2146       this->PostMessage(response.serialize().c_str());
2147     });
2148
2149     // Sync return
2150     ReportSuccess(out);
2151   }
2152 }
2153
2154 void FilesystemInstance::FileHandleWriteData(const picojson::value& args, picojson::object& out) {
2155   ScopeLogger();
2156   CHECK_EXIST(args, "id", out)
2157   CHECK_EXIST(args, "data", out)
2158   const auto& str = args.get("data").get<std::string>();
2159   const int fh_id = static_cast<int>(args.get("id").get<double>());
2160
2161   auto fh = opened_files.find(fh_id);
2162   if (opened_files.end() == fh) {
2163     LoggerE("FileHandle with id: %d not found", fh_id);
2164     LogAndReportError(IOException("Invalid FileHandle"), out);
2165     return;
2166   }
2167
2168   auto handle = fh->second;
2169
2170   auto logic = [str, handle](decltype(out) out) {
2171     try {
2172       std::vector<std::uint8_t> bytes;
2173       decode_binary_from_string(str, bytes);
2174       write_file(bytes.data(), bytes.size(), handle->file_handle);
2175     } catch (std::runtime_error& e) {
2176       LogAndReportError(IOException(e.what()), out);
2177     }
2178   };
2179
2180   bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
2181
2182   if (blocking) {
2183     logic(out);
2184   } else {
2185     // Async logic
2186     double callback_id = args.get("callbackId").get<double>();
2187     this->worker.add_job([this, callback_id, logic] {
2188       picojson::value response = picojson::value(picojson::object());
2189       picojson::object& async_out = response.get<picojson::object>();
2190       async_out["callbackId"] = picojson::value(callback_id);
2191       logic(async_out);
2192       this->PostMessage(response.serialize().c_str());
2193     });
2194
2195     // Sync return
2196     ReportSuccess(out);
2197   }
2198 }
2199
2200 void FilesystemInstance::FileHandleFlush(const picojson::value& args, picojson::object& out) {
2201   ScopeLogger();
2202   const int fh_id = static_cast<int>(args.get("id").get<double>());
2203
2204   auto fh = opened_files.find(fh_id);
2205   if (opened_files.end() == fh) {
2206     LogAndReportError(IOException("Invalid FileHandle"), out);
2207     return;
2208   }
2209
2210   auto handle = fh->second;
2211
2212   auto logic = [handle](decltype(out) out) {
2213     int ret = fflush(handle->file_handle);
2214     if (ret) {
2215       std::string error_message =
2216           std::string("flush failed, error message: ") + GetErrorString(errno);
2217       LogAndReportError(IOException(error_message.c_str()), out);
2218     }
2219   };
2220
2221   bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
2222
2223   if (blocking) {
2224     logic(out);
2225   } else {
2226     // Async logic
2227     double callback_id = args.get("callbackId").get<double>();
2228     this->worker.add_job([this, callback_id, logic] {
2229       picojson::value response = picojson::value(picojson::object());
2230       picojson::object& async_out = response.get<picojson::object>();
2231       async_out["callbackId"] = picojson::value(callback_id);
2232       logic(async_out);
2233       this->PostMessage(response.serialize().c_str());
2234     });
2235
2236     // Sync return
2237     ReportSuccess(out);
2238   }
2239 }
2240
2241 void FilesystemInstance::FileHandleSync(const picojson::value& args, picojson::object& out) {
2242   ScopeLogger();
2243   const int fh_id = static_cast<int>(args.get("id").get<double>());
2244
2245   auto fh = opened_files.find(fh_id);
2246   if (opened_files.end() == fh) {
2247     LogAndReportError(IOException("Invalid FileHandle"), out);
2248     return;
2249   }
2250
2251   auto handle = fh->second;
2252
2253   auto logic = [handle](decltype(out) out) {
2254     int ret = fsync(fileno(handle->file_handle));
2255     if (ret) {
2256       std::string error_message =
2257           std::string("sync failed, error message: ") + GetErrorString(errno);
2258       LogAndReportError(IOException(error_message.c_str()), out);
2259     }
2260   };
2261
2262   bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
2263
2264   if (blocking) {
2265     logic(out);
2266   } else {
2267     // Async logic
2268     double callback_id = args.get("callbackId").get<double>();
2269     this->worker.add_job([this, callback_id, logic] {
2270       picojson::value response = picojson::value(picojson::object());
2271       picojson::object& async_out = response.get<picojson::object>();
2272       async_out["callbackId"] = picojson::value(callback_id);
2273       logic(async_out);
2274       this->PostMessage(response.serialize().c_str());
2275     });
2276
2277     // Sync return
2278     ReportSuccess(out);
2279   }
2280 }
2281
2282 void FilesystemInstance::FileHandleClose(const picojson::value& args, picojson::object& out) {
2283   ScopeLogger();
2284   const int fh_id = static_cast<int>(args.get("id").get<double>());
2285   auto fh = opened_files.find(fh_id);
2286   if (opened_files.end() == fh) {
2287     LogAndReportError(IOException("Invalid FileHandle"), out);
2288     return;
2289   }
2290
2291   std::shared_ptr<FileHandle> handle = fh->second;
2292   opened_files.erase(fh);
2293
2294   auto logic = [handle](decltype(out) out) {
2295     if (!handle->file_handle) {
2296       LogAndReportError(IOException("File handle already closed."), out);
2297       return;
2298     }
2299     int ret = fclose(handle->file_handle);
2300     handle->file_handle = nullptr;
2301     if (ret) {
2302       std::string error_message =
2303           std::string("close failed, error message: ") + GetErrorString(errno);
2304       LogAndReportError(IOException(error_message.c_str()), out);
2305     }
2306   };
2307
2308   bool blocking = args.contains("blocking") ? args.get("blocking").get<bool>() : true;
2309
2310   if (blocking) {
2311     bool ready = false;
2312     bool done = false;
2313     std::mutex mutex;
2314     std::condition_variable conditional_variable;
2315     // adding empty job to worker's queue, in order to wait for all jobs to be done before closing
2316     // FILE*
2317     this->worker.add_job([] {},
2318                          [&conditional_variable, &mutex, &ready, &done, logic, &out] {
2319                            // wait for close
2320                            std::unique_lock<std::mutex> lock(mutex);
2321                            conditional_variable.wait(lock, [&ready] { return ready; });
2322
2323                            logic(out);
2324                            done = true;
2325                            conditional_variable.notify_one();
2326                          });
2327
2328     {
2329       // let know that close is ready
2330       std::unique_lock<std::mutex> lock(mutex);
2331       ready = true;
2332     }
2333     conditional_variable.notify_one();
2334
2335     {
2336       // wait for worker
2337       std::unique_lock<std::mutex> lock(mutex);
2338       conditional_variable.wait(lock, [&done] { return done; });
2339     }
2340     handle->file_handle = nullptr;
2341   } else {
2342     // Async logic
2343     double callback_id = args.get("callbackId").get<double>();
2344     this->worker.add_job([this, callback_id, logic] {
2345       picojson::value response = picojson::value(picojson::object());
2346       picojson::object& async_out = response.get<picojson::object>();
2347       async_out["callbackId"] = picojson::value(callback_id);
2348       logic(async_out);
2349       this->PostMessage(response.serialize().c_str());
2350     });
2351
2352     // Sync return
2353     ReportSuccess(out);
2354   }
2355 }
2356
2357 #undef CHECK_EXIST
2358
2359 }  // namespace filesystem
2360 }  // namespace extension