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