web_crawler_storage.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. #ifndef WEB_CRAWLER_STORAGE_H
  2. #define WEB_CRAWLER_STORAGE_H
  3. #include <furi.h>
  4. #include <storage/storage.h>
  5. #define SETTINGS_PATH STORAGE_EXT_PATH_PREFIX "/apps_data/" http_tag "/settings.bin"
  6. #define RECEIVED_DATA_PATH STORAGE_EXT_PATH_PREFIX "/apps_data/" http_tag "/" // add the file name to the end (e.g. "received_data.txt")
  7. // will need to make a duplicate of the file
  8. // one to save data, and the other for users to manipulate
  9. #define MAX_RECEIVED_DATA_SIZE 1024
  10. #define SHOW_MAX_FILE_SIZE 2048
  11. // Define the truncation notice
  12. #define TRUNCATION_NOTICE "\n\n[Data truncated due to size limits]"
  13. // Function to save settings: path, SSID, and password
  14. static void save_settings(const char *path, const char *ssid, const char *password, const char *file_rename, const char *file_type)
  15. {
  16. // Create the directory for saving settings
  17. char directory_path[256];
  18. snprintf(directory_path, sizeof(directory_path), STORAGE_EXT_PATH_PREFIX "/apps_data/web_crawler_app");
  19. // Create the directory
  20. Storage *storage = furi_record_open(RECORD_STORAGE);
  21. storage_common_mkdir(storage, directory_path);
  22. // Open the settings file
  23. File *file = storage_file_alloc(storage);
  24. if (!storage_file_open(file, SETTINGS_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS))
  25. {
  26. FURI_LOG_E(TAG, "Failed to open settings file for writing: %s", SETTINGS_PATH);
  27. storage_file_free(file);
  28. furi_record_close(RECORD_STORAGE);
  29. return;
  30. }
  31. if (file_type == NULL || strlen(file_type) == 0)
  32. {
  33. file_type = ".txt";
  34. }
  35. // Save the path length and data
  36. size_t path_length = strlen(path) + 1; // Include null terminator
  37. if (storage_file_write(file, &path_length, sizeof(size_t)) != sizeof(size_t) ||
  38. storage_file_write(file, path, path_length) != path_length)
  39. {
  40. FURI_LOG_E(TAG, "Failed to write path");
  41. }
  42. // Save the SSID length and data
  43. size_t ssid_length = strlen(ssid) + 1; // Include null terminator
  44. if (storage_file_write(file, &ssid_length, sizeof(size_t)) != sizeof(size_t) ||
  45. storage_file_write(file, ssid, ssid_length) != ssid_length)
  46. {
  47. FURI_LOG_E(TAG, "Failed to write SSID");
  48. }
  49. // Save the password length and data
  50. size_t password_length = strlen(password) + 1; // Include null terminator
  51. if (storage_file_write(file, &password_length, sizeof(size_t)) != sizeof(size_t) ||
  52. storage_file_write(file, password, password_length) != password_length)
  53. {
  54. FURI_LOG_E(TAG, "Failed to write password");
  55. }
  56. // Save the file rename length and data
  57. size_t file_rename_length = strlen(file_rename) + 1; // Include null terminator
  58. if (storage_file_write(file, &file_rename_length, sizeof(size_t)) != sizeof(size_t) ||
  59. storage_file_write(file, file_rename, file_rename_length) != file_rename_length)
  60. {
  61. FURI_LOG_E(TAG, "Failed to write file rename");
  62. }
  63. // Save the file type length and data
  64. size_t file_type_length = strlen(file_type) + 1; // Include null terminator
  65. if (storage_file_write(file, &file_type_length, sizeof(size_t)) != sizeof(size_t) ||
  66. storage_file_write(file, file_type, file_type_length) != file_type_length)
  67. {
  68. FURI_LOG_E(TAG, "Failed to write file type");
  69. }
  70. storage_file_close(file);
  71. storage_file_free(file);
  72. furi_record_close(RECORD_STORAGE);
  73. }
  74. // Function to load settings: path, SSID, and password
  75. static bool load_settings(
  76. char *path,
  77. size_t path_size,
  78. char *ssid,
  79. size_t ssid_size,
  80. char *password,
  81. size_t password_size,
  82. char *file_rename,
  83. size_t file_rename_size,
  84. char *file_type,
  85. size_t file_type_size,
  86. WebCrawlerApp *app)
  87. {
  88. if (!app)
  89. {
  90. FURI_LOG_E(TAG, "WebCrawlerApp is NULL");
  91. return false;
  92. }
  93. Storage *storage = furi_record_open(RECORD_STORAGE);
  94. File *file = storage_file_alloc(storage);
  95. if (!storage_file_open(file, SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING))
  96. {
  97. FURI_LOG_E(TAG, "Failed to open settings file for reading: %s", SETTINGS_PATH);
  98. storage_file_free(file);
  99. furi_record_close(RECORD_STORAGE);
  100. return false; // Return false if the file does not exist
  101. }
  102. // Load the path
  103. size_t path_length;
  104. if (storage_file_read(file, &path_length, sizeof(size_t)) != sizeof(size_t) || path_length > path_size ||
  105. storage_file_read(file, path, path_length) != path_length)
  106. {
  107. FURI_LOG_E(TAG, "Failed to read path");
  108. storage_file_close(file);
  109. storage_file_free(file);
  110. furi_record_close(RECORD_STORAGE);
  111. return false;
  112. }
  113. path[path_length - 1] = '\0'; // Ensure null-termination
  114. // Load the SSID
  115. size_t ssid_length;
  116. if (storage_file_read(file, &ssid_length, sizeof(size_t)) != sizeof(size_t) || ssid_length > ssid_size ||
  117. storage_file_read(file, ssid, ssid_length) != ssid_length)
  118. {
  119. FURI_LOG_E(TAG, "Failed to read SSID");
  120. storage_file_close(file);
  121. storage_file_free(file);
  122. furi_record_close(RECORD_STORAGE);
  123. return false;
  124. }
  125. ssid[ssid_length - 1] = '\0'; // Ensure null-termination
  126. // Load the password
  127. size_t password_length;
  128. if (storage_file_read(file, &password_length, sizeof(size_t)) != sizeof(size_t) || password_length > password_size ||
  129. storage_file_read(file, password, password_length) != password_length)
  130. {
  131. FURI_LOG_E(TAG, "Failed to read password");
  132. storage_file_close(file);
  133. storage_file_free(file);
  134. furi_record_close(RECORD_STORAGE);
  135. return false;
  136. }
  137. password[password_length - 1] = '\0'; // Ensure null-termination
  138. // Load the file rename
  139. size_t file_rename_length;
  140. if (storage_file_read(file, &file_rename_length, sizeof(size_t)) != sizeof(size_t) || file_rename_length > file_rename_size ||
  141. storage_file_read(file, file_rename, file_rename_length) != file_rename_length)
  142. {
  143. FURI_LOG_E(TAG, "Failed to read file rename");
  144. storage_file_close(file);
  145. storage_file_free(file);
  146. furi_record_close(RECORD_STORAGE);
  147. return false;
  148. }
  149. file_rename[file_rename_length - 1] = '\0'; // Ensure null-termination
  150. // Load the file type
  151. size_t file_type_length;
  152. if (storage_file_read(file, &file_type_length, sizeof(size_t)) != sizeof(size_t) || file_type_length > file_type_size ||
  153. storage_file_read(file, file_type, file_type_length) != file_type_length)
  154. {
  155. FURI_LOG_E(TAG, "Failed to read file type");
  156. storage_file_close(file);
  157. storage_file_free(file);
  158. furi_record_close(RECORD_STORAGE);
  159. return false;
  160. }
  161. file_type[file_type_length - 1] = '\0'; // Ensure null-termination
  162. // set the path, ssid, and password
  163. strncpy(app->path, path, path_size);
  164. strncpy(app->ssid, ssid, ssid_size);
  165. strncpy(app->password, password, password_size);
  166. strncpy(app->file_rename, file_rename, file_rename_size);
  167. strncpy(app->file_type, file_type, file_type_size);
  168. storage_file_close(file);
  169. storage_file_free(file);
  170. furi_record_close(RECORD_STORAGE);
  171. return true;
  172. }
  173. static bool delete_received_data(WebCrawlerApp *app)
  174. {
  175. if (app == NULL)
  176. {
  177. FURI_LOG_E(TAG, "WebCrawlerApp is NULL");
  178. return false;
  179. }
  180. // Open the storage record
  181. Storage *storage = furi_record_open(RECORD_STORAGE);
  182. if (!storage)
  183. {
  184. FURI_LOG_E(TAG, "Failed to open storage record");
  185. return false;
  186. }
  187. if (!storage_simply_remove_recursive(storage, RECEIVED_DATA_PATH "received_data.txt"))
  188. {
  189. FURI_LOG_E(TAG, "Failed to delete main file");
  190. furi_record_close(RECORD_STORAGE);
  191. return false;
  192. }
  193. // Allocate memory for new_path
  194. char *new_path = malloc(256);
  195. if (new_path == NULL)
  196. {
  197. FURI_LOG_E(TAG, "Memory allocation failed for paths");
  198. free(new_path);
  199. return false;
  200. }
  201. if (app->file_type == NULL || strlen(app->file_type) == 0)
  202. {
  203. app->file_type = ".txt";
  204. }
  205. // Format the new_path
  206. int ret_new = snprintf(new_path, 256, "%s%s%s", RECEIVED_DATA_PATH, app->file_rename, app->file_type);
  207. if (ret_new < 0 || (size_t)ret_new >= 256)
  208. {
  209. FURI_LOG_E(TAG, "Failed to create new_path");
  210. free(new_path);
  211. return false;
  212. }
  213. if (!storage_simply_remove_recursive(storage, new_path))
  214. {
  215. FURI_LOG_E(TAG, "Failed to delete duplicate file");
  216. furi_record_close(RECORD_STORAGE);
  217. return false;
  218. }
  219. furi_record_close(RECORD_STORAGE);
  220. return true;
  221. }
  222. static bool rename_received_data(const char *old_name, const char *new_name, const char *file_type, const char *old_file_type)
  223. {
  224. // Open the storage record
  225. Storage *storage = furi_record_open(RECORD_STORAGE);
  226. if (!storage)
  227. {
  228. FURI_LOG_E(TAG, "Failed to open storage record");
  229. return false;
  230. }
  231. // Allocate memory for old_path and new_path
  232. char *new_path = malloc(256);
  233. char *old_path = malloc(256);
  234. if (new_path == NULL || old_path == NULL)
  235. {
  236. FURI_LOG_E(TAG, "Memory allocation failed for paths");
  237. free(old_path);
  238. free(new_path);
  239. return false;
  240. }
  241. if (file_type == NULL || strlen(file_type) == 0)
  242. {
  243. file_type = ".txt";
  244. }
  245. if (old_file_type == NULL || strlen(old_file_type) == 0)
  246. {
  247. old_file_type = ".txt";
  248. }
  249. // Format the old_path
  250. int ret_old = snprintf(old_path, 256, "%s%s%s", RECEIVED_DATA_PATH, old_name, old_file_type);
  251. if (ret_old < 0 || (size_t)ret_old >= 256)
  252. {
  253. FURI_LOG_E(TAG, "Failed to create old_path");
  254. free(old_path);
  255. free(new_path);
  256. return false;
  257. }
  258. // Format the new_path
  259. int ret_new = snprintf(new_path, 256, "%s%s%s", RECEIVED_DATA_PATH, new_name, file_type);
  260. if (ret_new < 0 || (size_t)ret_new >= 256)
  261. {
  262. FURI_LOG_E(TAG, "Failed to create new_path");
  263. free(old_path);
  264. free(new_path);
  265. return false;
  266. }
  267. // Check if the file exists
  268. if (!storage_file_exists(storage, old_path))
  269. {
  270. if (!storage_file_exists(storage, RECEIVED_DATA_PATH "received_data.txt"))
  271. {
  272. FURI_LOG_E(TAG, "No saved file exists");
  273. free(old_path);
  274. free(new_path);
  275. furi_record_close(RECORD_STORAGE);
  276. return false;
  277. }
  278. else
  279. {
  280. bool renamed = storage_common_copy(storage, RECEIVED_DATA_PATH "received_data.txt", new_path) == FSE_OK;
  281. furi_record_close(RECORD_STORAGE);
  282. return renamed;
  283. }
  284. }
  285. else
  286. {
  287. bool renamed = storage_common_rename(storage, old_path, new_path) == FSE_OK;
  288. storage_simply_remove_recursive(storage, old_path);
  289. furi_record_close(RECORD_STORAGE);
  290. return renamed;
  291. }
  292. }
  293. static bool text_show_read_lines(File *file, FuriString *str_result)
  294. {
  295. // Reset the FuriString to ensure it's empty before reading
  296. furi_string_reset(str_result);
  297. // Define a buffer to hold the read data
  298. uint8_t buffer[SHOW_MAX_FILE_SIZE];
  299. // Read data into the buffer
  300. size_t read_count = storage_file_read(file, buffer, SHOW_MAX_FILE_SIZE);
  301. if (storage_file_get_error(file) != FSE_OK)
  302. {
  303. FURI_LOG_E(TAG, "Error reading from file.");
  304. return false;
  305. }
  306. // Append each byte to the FuriString
  307. for (size_t i = 0; i < read_count; i++)
  308. {
  309. furi_string_push_back(str_result, buffer[i]);
  310. }
  311. return true;
  312. }
  313. static bool load_received_data(WebCrawlerApp *app)
  314. {
  315. if (app == NULL)
  316. {
  317. FURI_LOG_E(TAG, "WebCrawlerApp is NULL");
  318. return false;
  319. }
  320. if (!app->widget_file_read)
  321. {
  322. FURI_LOG_E(TAG, "Textbox is NULL");
  323. return false;
  324. }
  325. // Open the storage record
  326. Storage *storage = furi_record_open(RECORD_STORAGE);
  327. if (!storage)
  328. {
  329. FURI_LOG_E(TAG, "Failed to open storage record");
  330. return false;
  331. }
  332. // Allocate a file handle
  333. File *file = storage_file_alloc(storage);
  334. if (!file)
  335. {
  336. FURI_LOG_E(TAG, "Failed to allocate storage file");
  337. furi_record_close(RECORD_STORAGE);
  338. return false;
  339. }
  340. // Open the file for reading
  341. if (!storage_file_open(file, RECEIVED_DATA_PATH "received_data.txt", FSAM_READ, FSOM_OPEN_EXISTING))
  342. {
  343. storage_file_free(file);
  344. furi_record_close(RECORD_STORAGE);
  345. return false; // Return false if the file does not exist
  346. }
  347. // Allocate a FuriString to hold the received data
  348. FuriString *str_result = furi_string_alloc();
  349. if (!str_result)
  350. {
  351. FURI_LOG_E(TAG, "Failed to allocate FuriString");
  352. storage_file_close(file);
  353. storage_file_free(file);
  354. furi_record_close(RECORD_STORAGE);
  355. return false;
  356. }
  357. // Read data into the FuriString
  358. bool read_success = text_show_read_lines(file, str_result);
  359. if (!read_success)
  360. {
  361. FURI_LOG_E(TAG, "Failed to read data from file");
  362. furi_string_free(str_result);
  363. storage_file_close(file);
  364. storage_file_free(file);
  365. furi_record_close(RECORD_STORAGE);
  366. return false;
  367. }
  368. // Check if there is more data beyond the maximum size
  369. char extra_byte;
  370. storage_file_read(file, &extra_byte, 1);
  371. // Retrieve the C-string from FuriString
  372. const char *data_cstr = furi_string_get_cstr(str_result);
  373. // Set the text box with the received data
  374. widget_reset(app->widget_file_read);
  375. if (str_result != NULL)
  376. {
  377. widget_add_text_scroll_element(
  378. app->widget_file_read,
  379. 0,
  380. 0,
  381. 128,
  382. 64, data_cstr);
  383. }
  384. else
  385. {
  386. widget_add_text_scroll_element(
  387. app->widget_file_read,
  388. 0,
  389. 0,
  390. 128,
  391. 64, "File is empty.");
  392. }
  393. // Clean up
  394. furi_string_free(str_result);
  395. storage_file_close(file);
  396. storage_file_free(file);
  397. furi_record_close(RECORD_STORAGE);
  398. return true;
  399. }
  400. #endif // WEB_CRAWLER_STORAGE_H