web_crawler_callback.c 75 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158
  1. #include <callback/web_crawler_callback.h>
  2. #include <storage/storage.h>
  3. #include <html/html_furi.h>
  4. // Below added by Derek Jamison
  5. // FURI_LOG_DEV will log only during app development. Be sure that Settings/System/Log Device is "LPUART"; so we dont use serial port.
  6. #ifdef DEVELOPMENT
  7. #define FURI_LOG_DEV(tag, format, ...) furi_log_print_format(FuriLogLevelInfo, tag, format, ##__VA_ARGS__)
  8. #define DEV_CRASH() furi_crash()
  9. #else
  10. #define FURI_LOG_DEV(tag, format, ...)
  11. #define DEV_CRASH()
  12. #endif
  13. static uint32_t web_crawler_back_to_file_callback(void *context);
  14. static bool alloc_widget(WebCrawlerApp *app, uint32_t view)
  15. {
  16. furi_check(app, "alloc_widget: app is NULL");
  17. if (!app->widget)
  18. {
  19. switch (view)
  20. {
  21. case WebCrawlerViewAbout:
  22. return easy_flipper_set_widget(&app->widget, WebCrawlerViewWidget, "Web Crawler App\n---\nBrowse the web, fetch API data, and more..\n---\nVisit github.com/jblanked for more details.\n---\nPress BACK to return.", web_crawler_back_to_main_callback, &app->view_dispatcher);
  23. case WebCrawlerViewFileRead:
  24. return easy_flipper_set_widget(&app->widget, WebCrawlerViewWidget, "Data will be displayed here.", web_crawler_back_to_file_callback, &app->view_dispatcher);
  25. case WebCrawlerViewFileDelete:
  26. return easy_flipper_set_widget(&app->widget, WebCrawlerViewWidget, "File deleted.", web_crawler_back_to_file_callback, &app->view_dispatcher);
  27. }
  28. }
  29. return false;
  30. }
  31. static void free_widget(WebCrawlerApp *app)
  32. {
  33. if (app->widget)
  34. {
  35. view_dispatcher_remove_view(app->view_dispatcher, WebCrawlerViewWidget);
  36. free(app->widget);
  37. app->widget = NULL;
  38. }
  39. }
  40. static bool alloc_submenu_config(WebCrawlerApp *app)
  41. {
  42. furi_check(app, "alloc_submenu_config: WebCrawlerApp is NULL");
  43. if (app->submenu_config)
  44. {
  45. FURI_LOG_E(TAG, "alloc_submenu_config: Submenu already allocated");
  46. return false;
  47. }
  48. if (easy_flipper_set_submenu(&app->submenu_config, WebCrawlerViewSubmenuConfig, "Settings", web_crawler_back_to_main_callback, &app->view_dispatcher))
  49. {
  50. submenu_add_item(app->submenu_config, "WiFi", WebCrawlerSubmenuIndexWifi, web_crawler_submenu_callback, app);
  51. submenu_add_item(app->submenu_config, "File", WebCrawlerSubmenuIndexFile, web_crawler_submenu_callback, app);
  52. submenu_add_item(app->submenu_config, "Request", WebCrawlerSubmenuIndexRequest, web_crawler_submenu_callback, app);
  53. return true;
  54. }
  55. return false;
  56. }
  57. static void free_submenu_config(WebCrawlerApp *app)
  58. {
  59. furi_check(app, "free_submenu_config: WebCrawlerApp is NULL");
  60. if (app->submenu_config)
  61. {
  62. view_dispatcher_remove_view(app->view_dispatcher, WebCrawlerViewSubmenuConfig);
  63. submenu_free(app->submenu_config);
  64. app->submenu_config = NULL;
  65. }
  66. }
  67. static bool alloc_variable_item_list(WebCrawlerApp *app, uint32_t view)
  68. {
  69. furi_check(app, "alloc_variable_item_list: WebCrawlerApp is NULL");
  70. if (app->variable_item_list)
  71. {
  72. FURI_LOG_E(TAG, "Variable Item List already allocated");
  73. return false;
  74. }
  75. bool settings_loaded = true;
  76. // load settings
  77. char path[128];
  78. char ssid[64];
  79. char password[64];
  80. char file_rename[128];
  81. char file_type[16];
  82. char http_method[16];
  83. char headers[256];
  84. char payload[256];
  85. if (!load_settings(path, 128, ssid, 64, password, 64, file_rename, 128, file_type, 16, http_method, 16, headers, 256, payload, 256, app))
  86. {
  87. FURI_LOG_E(TAG, "Failed to load settings");
  88. settings_loaded = false;
  89. }
  90. switch (view)
  91. {
  92. case WebCrawlerViewVariableItemListWifi:
  93. if (!easy_flipper_set_variable_item_list(&app->variable_item_list, WebCrawlerViewVariableItemList, web_crawler_wifi_enter_callback, web_crawler_back_to_configure_callback, &app->view_dispatcher, app))
  94. {
  95. return false;
  96. }
  97. if (!app->ssid_item)
  98. {
  99. app->ssid_item = variable_item_list_add(app->variable_item_list, "SSID", 0, NULL, NULL); // index 0
  100. variable_item_set_current_value_text(app->ssid_item, ""); // Initialize
  101. }
  102. if (!app->password_item)
  103. {
  104. app->password_item = variable_item_list_add(app->variable_item_list, "Password", 0, NULL, NULL); // index 1
  105. variable_item_set_current_value_text(app->password_item, ""); // Initialize
  106. }
  107. if (settings_loaded)
  108. {
  109. variable_item_set_current_value_text(app->ssid_item, ssid);
  110. // variable_item_set_current_value_text(app->password_item, password);
  111. }
  112. else
  113. {
  114. variable_item_set_current_value_text(app->ssid_item, ""); // Initialize
  115. // variable_item_set_current_value_text(app->password_item, "Password"); // Initialize
  116. }
  117. // save for updating temp buffers later
  118. save_char("ssid", ssid);
  119. save_char("password", password);
  120. // strncpy(app->temp_buffer_ssid, app->ssid, app->temp_buffer_size_ssid - 1);
  121. // app->temp_buffer_ssid[app->temp_buffer_size_ssid - 1] = '\0';
  122. // strncpy(app->temp_buffer_password, app->password, app->temp_buffer_size_password - 1);
  123. // app->temp_buffer_password[app->temp_buffer_size_password - 1] = '\0';
  124. break;
  125. case WebCrawlerViewVariableItemListFile:
  126. if (!easy_flipper_set_variable_item_list(&app->variable_item_list, WebCrawlerViewVariableItemList, web_crawler_file_enter_callback, web_crawler_back_to_configure_callback, &app->view_dispatcher, app))
  127. {
  128. return false;
  129. }
  130. if (!app->file_read_item)
  131. {
  132. app->file_read_item = variable_item_list_add(app->variable_item_list, "Read File", 0, NULL, NULL); // index 0
  133. variable_item_set_current_value_text(app->file_read_item, ""); // Initialize
  134. }
  135. if (!app->file_type_item)
  136. {
  137. app->file_type_item = variable_item_list_add(app->variable_item_list, "Set File Type", 0, NULL, NULL); // index 1
  138. variable_item_set_current_value_text(app->file_type_item, ""); // Initialize
  139. }
  140. if (!app->file_rename_item)
  141. {
  142. app->file_rename_item = variable_item_list_add(app->variable_item_list, "Rename File", 0, NULL, NULL); // index 2
  143. variable_item_set_current_value_text(app->file_rename_item, ""); // Initialize
  144. }
  145. if (!app->file_delete_item)
  146. {
  147. app->file_delete_item = variable_item_list_add(app->variable_item_list, "Delete File", 0, NULL, NULL); // index 3
  148. variable_item_set_current_value_text(app->file_delete_item, ""); // Initialize
  149. }
  150. if (settings_loaded)
  151. {
  152. variable_item_set_current_value_text(app->file_type_item, file_type);
  153. variable_item_set_current_value_text(app->file_rename_item, file_rename);
  154. }
  155. else
  156. {
  157. variable_item_set_current_value_text(app->file_type_item, ".txt"); // Initialize
  158. variable_item_set_current_value_text(app->file_rename_item, "received_data"); // Initialize
  159. }
  160. // save for updating temp buffers later
  161. save_char("file_type", file_type);
  162. save_char("file_rename", file_rename);
  163. // strncpy(app->temp_buffer_file_type, app->file_type, app->temp_buffer_size_file_type - 1);
  164. // app->temp_buffer_file_type[app->temp_buffer_size_file_type - 1] = '\0';
  165. // strncpy(app->temp_buffer_file_rename, app->file_rename, app->temp_buffer_size_file_rename - 1);
  166. // app->temp_buffer_file_rename[app->temp_buffer_size_file_rename - 1] = '\0';
  167. break;
  168. case WebCrawlerViewVariableItemListRequest:
  169. if (!easy_flipper_set_variable_item_list(&app->variable_item_list, WebCrawlerViewVariableItemList, web_crawler_request_enter_callback, web_crawler_back_to_configure_callback, &app->view_dispatcher, app))
  170. {
  171. return false;
  172. }
  173. if (!app->path_item)
  174. {
  175. app->path_item = variable_item_list_add(app->variable_item_list, "Path", 0, NULL, NULL);
  176. variable_item_set_current_value_text(app->path_item, ""); // Initialize
  177. }
  178. if (!app->http_method_item)
  179. {
  180. app->http_method_item = variable_item_list_add(app->variable_item_list, "HTTP Method", 6, web_crawler_http_method_change, app);
  181. variable_item_set_current_value_text(app->http_method_item, ""); // Initialize
  182. variable_item_set_current_value_index(app->http_method_item, 0); // Initialize
  183. }
  184. if (!app->headers_item)
  185. {
  186. app->headers_item = variable_item_list_add(app->variable_item_list, "Headers", 0, NULL, NULL);
  187. variable_item_set_current_value_text(app->headers_item, ""); // Initialize
  188. }
  189. if (!app->payload_item)
  190. {
  191. app->payload_item = variable_item_list_add(app->variable_item_list, "Payload", 0, NULL, NULL);
  192. variable_item_set_current_value_text(app->payload_item, ""); // Initialize
  193. }
  194. //
  195. //
  196. if (settings_loaded)
  197. {
  198. variable_item_set_current_value_text(app->path_item, path);
  199. variable_item_set_current_value_text(app->http_method_item, http_method);
  200. variable_item_set_current_value_text(app->headers_item, headers);
  201. variable_item_set_current_value_text(app->payload_item, payload);
  202. //
  203. variable_item_set_current_value_index(
  204. app->http_method_item,
  205. strstr(http_method, "GET") != NULL ? 0 : strstr(http_method, "POST") != NULL ? 1
  206. : strstr(http_method, "PUT") != NULL ? 2
  207. : strstr(http_method, "DELETE") != NULL ? 3
  208. : strstr(http_method, "DOWNLOAD") != NULL ? 4
  209. : strstr(http_method, "BROWSE") != NULL ? 5
  210. : 0);
  211. }
  212. else
  213. {
  214. variable_item_set_current_value_text(app->path_item, "https://httpbin.org/get"); // Initialize
  215. variable_item_set_current_value_text(app->http_method_item, "GET"); // Initialize
  216. variable_item_set_current_value_text(app->headers_item, "{\n\t\"Content-Type\": \"application/json\"\n}"); // Initialize
  217. variable_item_set_current_value_text(app->payload_item, "{\n\t\"key\": \"value\"\n}"); // Initialize
  218. }
  219. // save for updating temp buffers later
  220. save_char("path", path);
  221. save_char("http_method", http_method);
  222. save_char("headers", headers);
  223. save_char("payload", payload);
  224. // strncpy(app->temp_buffer_path, app->path, app->temp_buffer_size_path - 1);
  225. // app->temp_buffer_path[app->temp_buffer_size_path - 1] = '\0';
  226. // strncpy(app->temp_buffer_http_method, app->http_method, app->temp_buffer_size_http_method - 1);
  227. // app->temp_buffer_http_method[app->temp_buffer_size_http_method - 1] = '\0';
  228. // strncpy(app->temp_buffer_headers, app->headers, app->temp_buffer_size_headers - 1);
  229. // app->temp_buffer_headers[app->temp_buffer_size_headers - 1] = '\0';
  230. // strncpy(app->temp_buffer_payload, app->payload, app->temp_buffer_size_payload - 1);
  231. // app->temp_buffer_payload[app->temp_buffer_size_payload - 1] = '\0';
  232. break;
  233. default:
  234. FURI_LOG_E(TAG, "Invalid view");
  235. return false;
  236. }
  237. return true;
  238. }
  239. static void free_variable_item_list(WebCrawlerApp *app)
  240. {
  241. furi_check(app, "free_variable_item_list: WebCrawlerApp is NULL");
  242. if (app->variable_item_list)
  243. {
  244. view_dispatcher_remove_view(app->view_dispatcher, WebCrawlerViewVariableItemList);
  245. variable_item_list_free(app->variable_item_list);
  246. app->variable_item_list = NULL;
  247. }
  248. // check and free variable items
  249. if (app->ssid_item)
  250. {
  251. free(app->ssid_item);
  252. app->ssid_item = NULL;
  253. }
  254. if (app->password_item)
  255. {
  256. free(app->password_item);
  257. app->password_item = NULL;
  258. }
  259. if (app->file_type_item)
  260. {
  261. free(app->file_type_item);
  262. app->file_type_item = NULL;
  263. }
  264. if (app->file_rename_item)
  265. {
  266. free(app->file_rename_item);
  267. app->file_rename_item = NULL;
  268. }
  269. if (app->file_read_item)
  270. {
  271. free(app->file_read_item);
  272. app->file_read_item = NULL;
  273. }
  274. if (app->file_delete_item)
  275. {
  276. free(app->file_delete_item);
  277. app->file_delete_item = NULL;
  278. }
  279. if (app->path_item)
  280. {
  281. free(app->path_item);
  282. app->path_item = NULL;
  283. }
  284. if (app->http_method_item)
  285. {
  286. free(app->http_method_item);
  287. app->http_method_item = NULL;
  288. }
  289. if (app->headers_item)
  290. {
  291. free(app->headers_item);
  292. app->headers_item = NULL;
  293. }
  294. if (app->payload_item)
  295. {
  296. free(app->payload_item);
  297. app->payload_item = NULL;
  298. }
  299. }
  300. static bool alloc_text_input(WebCrawlerApp *app, uint32_t view)
  301. {
  302. furi_check(app, "alloc_text_input: WebCrawlerApp is NULL");
  303. if (app->uart_text_input)
  304. {
  305. FURI_LOG_E(TAG, "Text Input already allocated");
  306. return false;
  307. }
  308. switch (view)
  309. {
  310. case WebCrawlerViewTextInput:
  311. app->temp_buffer_size_path = 128;
  312. if (!easy_flipper_set_buffer(&app->temp_buffer_path, app->temp_buffer_size_path) || !easy_flipper_set_buffer(&app->path, app->temp_buffer_size_path))
  313. {
  314. return false;
  315. }
  316. if (!easy_flipper_set_uart_text_input(&app->uart_text_input, WebCrawlerViewInput, "Enter URL", app->temp_buffer_path, app->temp_buffer_size_path, NULL, web_crawler_back_to_request_callback, &app->view_dispatcher, app))
  317. {
  318. return false;
  319. }
  320. if (load_char("path", app->path, app->temp_buffer_size_path))
  321. {
  322. snprintf(app->temp_buffer_path, app->temp_buffer_size_path, "%s", app->path);
  323. }
  324. break;
  325. case WebCrawlerViewTextInputSSID:
  326. app->temp_buffer_size_ssid = 64;
  327. if (!easy_flipper_set_buffer(&app->temp_buffer_ssid, app->temp_buffer_size_ssid) || !easy_flipper_set_buffer(&app->ssid, app->temp_buffer_size_ssid))
  328. {
  329. return false;
  330. }
  331. if (!easy_flipper_set_uart_text_input(&app->uart_text_input, WebCrawlerViewInput, "Enter SSID", app->temp_buffer_ssid, app->temp_buffer_size_ssid, NULL, web_crawler_back_to_wifi_callback, &app->view_dispatcher, app))
  332. {
  333. return false;
  334. }
  335. if (load_char("ssid", app->ssid, app->temp_buffer_size_ssid))
  336. {
  337. snprintf(app->temp_buffer_ssid, app->temp_buffer_size_ssid, "%s", app->ssid);
  338. }
  339. break;
  340. case WebCrawlerViewTextInputPassword:
  341. app->temp_buffer_size_password = 64;
  342. if (!easy_flipper_set_buffer(&app->temp_buffer_password, app->temp_buffer_size_password) || !easy_flipper_set_buffer(&app->password, app->temp_buffer_size_password))
  343. {
  344. return false;
  345. }
  346. if (!easy_flipper_set_uart_text_input(&app->uart_text_input, WebCrawlerViewInput, "Enter Password", app->temp_buffer_password, app->temp_buffer_size_password, NULL, web_crawler_back_to_wifi_callback, &app->view_dispatcher, app))
  347. {
  348. return false;
  349. }
  350. if (load_char("password", app->password, app->temp_buffer_size_password))
  351. {
  352. snprintf(app->temp_buffer_password, app->temp_buffer_size_password, "%s", app->password);
  353. }
  354. break;
  355. case WebCrawlerViewTextInputFileType:
  356. app->temp_buffer_size_file_type = 16;
  357. if (!easy_flipper_set_buffer(&app->temp_buffer_file_type, app->temp_buffer_size_file_type) || !easy_flipper_set_buffer(&app->file_type, app->temp_buffer_size_file_type))
  358. {
  359. return false;
  360. }
  361. if (!easy_flipper_set_uart_text_input(&app->uart_text_input, WebCrawlerViewInput, "Enter File Type", app->temp_buffer_file_type, app->temp_buffer_size_file_type, NULL, web_crawler_back_to_file_callback, &app->view_dispatcher, app))
  362. {
  363. return false;
  364. }
  365. if (load_char("file_type", app->file_type, app->temp_buffer_size_file_type))
  366. {
  367. snprintf(app->temp_buffer_file_type, app->temp_buffer_size_file_type, "%s", app->file_type);
  368. }
  369. break;
  370. case WebCrawlerViewTextInputFileRename:
  371. app->temp_buffer_size_file_rename = 128;
  372. if (!easy_flipper_set_buffer(&app->temp_buffer_file_rename, app->temp_buffer_size_file_rename) || !easy_flipper_set_buffer(&app->file_rename, app->temp_buffer_size_file_rename))
  373. {
  374. return false;
  375. }
  376. if (!easy_flipper_set_uart_text_input(&app->uart_text_input, WebCrawlerViewInput, "Enter File Rename", app->temp_buffer_file_rename, app->temp_buffer_size_file_rename, NULL, web_crawler_back_to_file_callback, &app->view_dispatcher, app))
  377. {
  378. return false;
  379. }
  380. if (load_char("file_rename", app->file_rename, app->temp_buffer_size_file_rename))
  381. {
  382. snprintf(app->temp_buffer_file_rename, app->temp_buffer_size_file_rename, "%s", app->file_rename);
  383. }
  384. break;
  385. case WebCrawlerViewTextInputHeaders:
  386. app->temp_buffer_size_headers = 256;
  387. if (!easy_flipper_set_buffer(&app->temp_buffer_headers, app->temp_buffer_size_headers) || !easy_flipper_set_buffer(&app->headers, app->temp_buffer_size_headers))
  388. {
  389. return false;
  390. }
  391. if (!easy_flipper_set_uart_text_input(&app->uart_text_input, WebCrawlerViewInput, "Enter Headers", app->temp_buffer_headers, app->temp_buffer_size_headers, NULL, web_crawler_back_to_request_callback, &app->view_dispatcher, app))
  392. {
  393. return false;
  394. }
  395. if (load_char("headers", app->headers, app->temp_buffer_size_headers))
  396. {
  397. snprintf(app->temp_buffer_headers, app->temp_buffer_size_headers, "%s", app->headers);
  398. }
  399. break;
  400. case WebCrawlerViewTextInputPayload:
  401. app->temp_buffer_size_payload = 256;
  402. if (!easy_flipper_set_buffer(&app->temp_buffer_payload, app->temp_buffer_size_payload) || !easy_flipper_set_buffer(&app->payload, app->temp_buffer_size_payload))
  403. {
  404. return false;
  405. }
  406. if (!easy_flipper_set_uart_text_input(&app->uart_text_input, WebCrawlerViewInput, "Enter Payload", app->temp_buffer_payload, app->temp_buffer_size_payload, NULL, web_crawler_back_to_request_callback, &app->view_dispatcher, app))
  407. {
  408. return false;
  409. }
  410. if (load_char("payload", app->payload, app->temp_buffer_size_payload))
  411. {
  412. snprintf(app->temp_buffer_payload, app->temp_buffer_size_payload, "%s", app->payload);
  413. }
  414. break;
  415. default:
  416. FURI_LOG_E(TAG, "Invalid view");
  417. return false;
  418. }
  419. return true;
  420. }
  421. static void free_text_input(WebCrawlerApp *app)
  422. {
  423. furi_check(app, "free_text_input: WebCrawlerApp is NULL");
  424. if (app->uart_text_input)
  425. {
  426. view_dispatcher_remove_view(app->view_dispatcher, WebCrawlerViewInput);
  427. uart_text_input_free(app->uart_text_input);
  428. app->uart_text_input = NULL;
  429. }
  430. // check and free path
  431. if (app->temp_buffer_path)
  432. {
  433. free(app->temp_buffer_path);
  434. app->temp_buffer_path = NULL;
  435. }
  436. if (app->path)
  437. {
  438. free(app->path);
  439. app->path = NULL;
  440. }
  441. // check and free ssid
  442. if (app->temp_buffer_ssid)
  443. {
  444. free(app->temp_buffer_ssid);
  445. app->temp_buffer_ssid = NULL;
  446. }
  447. if (app->ssid)
  448. {
  449. free(app->ssid);
  450. app->ssid = NULL;
  451. }
  452. // check and free password
  453. if (app->temp_buffer_password)
  454. {
  455. free(app->temp_buffer_password);
  456. app->temp_buffer_password = NULL;
  457. }
  458. if (app->password)
  459. {
  460. free(app->password);
  461. app->password = NULL;
  462. }
  463. // check and free file type
  464. if (app->temp_buffer_file_type)
  465. {
  466. free(app->temp_buffer_file_type);
  467. app->temp_buffer_file_type = NULL;
  468. }
  469. if (app->file_type)
  470. {
  471. free(app->file_type);
  472. app->file_type = NULL;
  473. }
  474. // check and free file rename
  475. if (app->temp_buffer_file_rename)
  476. {
  477. free(app->temp_buffer_file_rename);
  478. app->temp_buffer_file_rename = NULL;
  479. }
  480. if (app->file_rename)
  481. {
  482. free(app->file_rename);
  483. app->file_rename = NULL;
  484. }
  485. // check and free headers
  486. if (app->temp_buffer_headers)
  487. {
  488. free(app->temp_buffer_headers);
  489. app->temp_buffer_headers = NULL;
  490. }
  491. if (app->headers)
  492. {
  493. free(app->headers);
  494. app->headers = NULL;
  495. }
  496. // check and free payload
  497. if (app->temp_buffer_payload)
  498. {
  499. free(app->temp_buffer_payload);
  500. app->temp_buffer_payload = NULL;
  501. }
  502. if (app->payload)
  503. {
  504. free(app->payload);
  505. app->payload = NULL;
  506. }
  507. }
  508. void free_all(WebCrawlerApp *app)
  509. {
  510. furi_check(app, "free_all: app is NULL");
  511. free_widget(app);
  512. free_submenu_config(app);
  513. free_variable_item_list(app);
  514. free_text_input(app);
  515. }
  516. static void web_crawler_draw_error(Canvas *canvas, DataLoaderModel *model)
  517. {
  518. furi_check(model, "web_crawler_draw_error: DataLoaderModel is NULL");
  519. furi_check(model->fhttp, "web_crawler_draw_error: FlipperHTTP is NULL");
  520. furi_check(canvas, "Canvas is NULL");
  521. canvas_clear(canvas);
  522. canvas_set_font(canvas, FontSecondary);
  523. if (model->fhttp->state == INACTIVE)
  524. {
  525. canvas_draw_str(canvas, 0, 7, "Wifi Dev Board disconnected.");
  526. canvas_draw_str(canvas, 0, 17, "Please connect to the board.");
  527. canvas_draw_str(canvas, 0, 32, "If your board is connected,");
  528. canvas_draw_str(canvas, 0, 42, "make sure you have flashed");
  529. canvas_draw_str(canvas, 0, 52, "your WiFi Devboard with the");
  530. canvas_draw_str(canvas, 0, 62, "latest FlipperHTTP flash.");
  531. return;
  532. }
  533. if (model->fhttp->last_response)
  534. {
  535. if (strstr(model->fhttp->last_response, "[ERROR] Not connected to Wifi. Failed to reconnect.") != NULL)
  536. {
  537. canvas_draw_str(canvas, 0, 10, "[ERROR] Not connected to Wifi.");
  538. canvas_draw_str(canvas, 0, 50, "Update your WiFi settings.");
  539. canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
  540. return;
  541. }
  542. if (strstr(model->fhttp->last_response, "[ERROR] Failed to connect to Wifi.") != NULL)
  543. {
  544. canvas_draw_str(canvas, 0, 10, "[ERROR] Not connected to Wifi.");
  545. canvas_draw_str(canvas, 0, 50, "Update your WiFi settings.");
  546. canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
  547. return;
  548. }
  549. if (strstr(model->fhttp->last_response, "[ERROR] GET request failed with error: connection refused") != NULL)
  550. {
  551. canvas_draw_str(canvas, 0, 10, "[ERROR] Connection refused.");
  552. canvas_draw_str(canvas, 0, 50, "Choose another URL.");
  553. canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
  554. return;
  555. }
  556. if (strstr(model->fhttp->last_response, "[PONG]") != NULL)
  557. {
  558. canvas_clear(canvas);
  559. canvas_draw_str(canvas, 0, 10, "[STATUS]Connecting to AP...");
  560. return;
  561. }
  562. canvas_draw_str(canvas, 0, 10, "[ERROR] Failed to sync data.");
  563. canvas_draw_str(canvas, 0, 30, "If this is your third attempt,");
  564. canvas_draw_str(canvas, 0, 40, "it's likely your URL is not");
  565. canvas_draw_str(canvas, 0, 50, "compabilbe or correct.");
  566. canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
  567. return;
  568. }
  569. canvas_draw_str(canvas, 0, 10, "HTTP request failed.");
  570. canvas_draw_str(canvas, 0, 20, "Press BACK to return.");
  571. }
  572. static void save_simply()
  573. {
  574. char path[128];
  575. char ssid[64];
  576. char password[64];
  577. char file_rename[128];
  578. char file_type[16];
  579. char http_method[16];
  580. char headers[256];
  581. char payload[256];
  582. if (load_char("path", path, 128) &&
  583. load_char("ssid", ssid, 64) &&
  584. load_char("password", password, 64) &&
  585. load_char("file_rename", file_rename, 128) &&
  586. load_char("file_type", file_type, 16) &&
  587. load_char("http_method", http_method, 16) &&
  588. load_char("headers", headers, 256) &&
  589. load_char("payload", payload, 256))
  590. {
  591. save_settings(path, ssid, password, file_rename, file_type, http_method, headers, payload);
  592. }
  593. }
  594. void web_crawler_http_method_change(VariableItem *item)
  595. {
  596. WebCrawlerApp *app = (WebCrawlerApp *)variable_item_get_context(item);
  597. furi_check(app, "web_crawler_http_method_change: WebCrawlerApp is NULL");
  598. uint8_t index = variable_item_get_current_value_index(item);
  599. variable_item_set_current_value_text(item, http_method_names[index]);
  600. variable_item_set_current_value_index(item, index);
  601. // save the http method
  602. save_char("http_method", http_method_names[index]);
  603. save_simply();
  604. }
  605. static bool web_crawler_fetch(DataLoaderModel *model)
  606. {
  607. WebCrawlerApp *app = (WebCrawlerApp *)model->parser_context;
  608. furi_check(app, "web_crawler_fetch: WebCrawlerApp is NULL");
  609. furi_check(model->fhttp, "web_crawler_fetch: FlipperHTTP is NULL");
  610. char url[128];
  611. if (!load_char("path", url, 128))
  612. {
  613. return false;
  614. }
  615. char file_type[16];
  616. if (!load_char("file_type", file_type, 16))
  617. {
  618. return false;
  619. }
  620. char file_rename[128];
  621. if (!load_char("file_rename", file_rename, 128))
  622. {
  623. return false;
  624. }
  625. char http_method[16];
  626. if (!load_char("http_method", http_method, 16))
  627. {
  628. return false;
  629. }
  630. char headers[256];
  631. if (!load_char("headers", headers, 256))
  632. {
  633. return false;
  634. }
  635. char payload[256];
  636. if (!load_char("payload", payload, 256))
  637. {
  638. return false;
  639. }
  640. if (strlen(file_rename) > 0 && strlen(file_type) > 0)
  641. {
  642. snprintf(
  643. model->fhttp->file_path,
  644. sizeof(model->fhttp->file_path),
  645. STORAGE_EXT_PATH_PREFIX "/apps_data/web_crawler/%s%s",
  646. file_rename,
  647. file_type);
  648. }
  649. else
  650. {
  651. snprintf(
  652. model->fhttp->file_path,
  653. sizeof(model->fhttp->file_path),
  654. STORAGE_EXT_PATH_PREFIX "/apps_data/web_crawler/received_data.txt");
  655. }
  656. if (strstr(http_method, "GET") != NULL)
  657. {
  658. model->fhttp->save_received_data = true;
  659. model->fhttp->is_bytes_request = false;
  660. // Perform GET request and handle the response
  661. if (strlen(headers) == 0)
  662. {
  663. return flipper_http_get_request(model->fhttp, url);
  664. }
  665. else
  666. {
  667. return flipper_http_get_request_with_headers(model->fhttp, url, headers);
  668. }
  669. }
  670. else if (strstr(http_method, "POST") != NULL)
  671. {
  672. model->fhttp->save_received_data = true;
  673. model->fhttp->is_bytes_request = false;
  674. // Perform POST request and handle the response
  675. return flipper_http_post_request_with_headers(model->fhttp, url, headers, payload);
  676. }
  677. else if (strstr(http_method, "PUT") != NULL)
  678. {
  679. model->fhttp->save_received_data = true;
  680. model->fhttp->is_bytes_request = false;
  681. // Perform PUT request and handle the response
  682. return flipper_http_put_request_with_headers(model->fhttp, url, headers, payload);
  683. }
  684. else if (strstr(http_method, "DELETE") != NULL)
  685. {
  686. model->fhttp->save_received_data = true;
  687. model->fhttp->is_bytes_request = false;
  688. // Perform DELETE request and handle the response
  689. return flipper_http_delete_request_with_headers(model->fhttp, url, headers, payload);
  690. }
  691. else if (strstr(http_method, "DOWNLOAD") != NULL)
  692. {
  693. model->fhttp->save_received_data = false;
  694. model->fhttp->is_bytes_request = true;
  695. // Perform GET request and handle the response
  696. return flipper_http_get_request_bytes(model->fhttp, url, "{\"Content-Type\": \"application/octet-stream\"}");
  697. }
  698. else // BROWSE
  699. {
  700. model->fhttp->save_received_data = false;
  701. model->fhttp->is_bytes_request = true;
  702. // download HTML response since the html could be large
  703. return flipper_http_get_request_bytes(model->fhttp, url, "{\"Content-Type\": \"application/octet-stream\"}");
  704. }
  705. return false;
  706. }
  707. static char *web_crawler_parse(DataLoaderModel *model)
  708. {
  709. UNUSED(model);
  710. // parse HTML response if BROWSE request
  711. char http_method[16];
  712. if (!load_char("http_method", http_method, 16))
  713. {
  714. FURI_LOG_E(TAG, "Failed to load http method");
  715. }
  716. else
  717. {
  718. if (strstr(http_method, "BROWSE") != NULL)
  719. {
  720. // parse HTML then return response
  721. FuriString *returned_data = flipper_http_load_from_file(model->fhttp->file_path);
  722. if (returned_data == NULL || furi_string_size(returned_data) == 0)
  723. {
  724. return "Failed to load HTML response.\n\n\n\n\nPress BACK to return.";
  725. }
  726. // head is mandatory,
  727. bool head_exists = html_furi_tag_exists("<head>", returned_data, 0);
  728. if (!head_exists)
  729. {
  730. FURI_LOG_E(TAG, "Invalid HTML response");
  731. return "Invalid HTML response.\n\n\n\n\nPress BACK to return.";
  732. }
  733. // optional tags but we'll append them the response in order (title -> h1 -> h2 -> h3 -> p)
  734. bool title_exists = html_furi_tag_exists("<title>", returned_data, 0);
  735. bool h1_exists = html_furi_tag_exists("<h1>", returned_data, 0);
  736. bool h2_exists = html_furi_tag_exists("<h2>", returned_data, 0);
  737. bool h3_exists = html_furi_tag_exists("<h3>", returned_data, 0);
  738. bool p_exists = html_furi_tag_exists("<p>", returned_data, 0);
  739. FuriString *response = furi_string_alloc();
  740. if (title_exists)
  741. {
  742. FuriString *title = html_furi_find_tag("<title>", returned_data, 0);
  743. furi_string_cat_str(response, "Title: ");
  744. furi_string_cat(response, title);
  745. furi_string_cat_str(response, "\n\n");
  746. furi_string_free(title);
  747. }
  748. if (h1_exists)
  749. {
  750. FuriString *h1 = html_furi_find_tag("<h1>", returned_data, 0);
  751. furi_string_cat(response, h1);
  752. furi_string_cat_str(response, "\n\n");
  753. furi_string_free(h1);
  754. }
  755. if (h2_exists)
  756. {
  757. FuriString *h2 = html_furi_find_tag("<h2>", returned_data, 0);
  758. furi_string_cat(response, h2);
  759. furi_string_cat_str(response, "\n");
  760. furi_string_free(h2);
  761. }
  762. if (h3_exists)
  763. {
  764. FuriString *h3 = html_furi_find_tag("<h3>", returned_data, 0);
  765. furi_string_cat(response, h3);
  766. furi_string_cat_str(response, "\n");
  767. furi_string_free(h3);
  768. }
  769. if (p_exists)
  770. {
  771. FuriString *p = html_furi_find_tags("<p>", returned_data);
  772. furi_string_cat(response, p);
  773. furi_string_free(p);
  774. }
  775. furi_string_free(returned_data);
  776. if (response && furi_string_size(response) > 0)
  777. {
  778. return (char *)furi_string_get_cstr(response);
  779. }
  780. return "No HTML tags found.\nTry another URL...\n\n\n\nPress BACK to return.";
  781. }
  782. }
  783. return "Data saved to file.\n\n\n\n\nPress BACK to return.";
  784. }
  785. static void web_crawler_data_switch_to_view(WebCrawlerApp *app)
  786. {
  787. furi_check(app, "web_crawler_data_switch_to_view: WebCrawlerApp is NULL");
  788. // Allocate title on the heap.
  789. char *title = malloc(32);
  790. if (title == NULL)
  791. {
  792. FURI_LOG_E(TAG, "Failed to allocate memory for title");
  793. return; // or handle the error as needed
  794. }
  795. char http_method[16];
  796. if (!load_char("http_method", http_method, sizeof(http_method)))
  797. {
  798. FURI_LOG_E(TAG, "Failed to load http method");
  799. snprintf(title, 32, "Request");
  800. }
  801. else
  802. {
  803. if (strstr(http_method, "GET") != NULL)
  804. {
  805. snprintf(title, 32, "GET Request");
  806. }
  807. else if (strstr(http_method, "POST") != NULL)
  808. {
  809. snprintf(title, 32, "POST Request");
  810. }
  811. else if (strstr(http_method, "PUT") != NULL)
  812. {
  813. snprintf(title, 32, "PUT Request");
  814. }
  815. else if (strstr(http_method, "DELETE") != NULL)
  816. {
  817. snprintf(title, 32, "DELETE Request");
  818. }
  819. else if (strstr(http_method, "DOWNLOAD") != NULL)
  820. {
  821. snprintf(title, 32, "File Download");
  822. }
  823. else if (strstr(http_method, "BROWSE") != NULL)
  824. {
  825. snprintf(title, 32, "Browse URL");
  826. }
  827. else
  828. {
  829. // Provide a default title if no known http method is found.
  830. snprintf(title, 32, "Request");
  831. }
  832. }
  833. web_crawler_generic_switch_to_view(app, title, web_crawler_fetch, web_crawler_parse, 1, web_crawler_back_to_main_callback, WebCrawlerViewLoader);
  834. }
  835. /**
  836. * @brief Navigation callback to handle exiting from other views to the submenu.
  837. * @param context The context - WebCrawlerApp object.
  838. * @return WebCrawlerViewSubmenu
  839. */
  840. uint32_t web_crawler_back_to_configure_callback(void *context)
  841. {
  842. WebCrawlerApp *app = (WebCrawlerApp *)context;
  843. furi_check(app, "web_crawler_back_to_configure_callback: WebCrawlerApp is NULL");
  844. return WebCrawlerViewSubmenuConfig; // Return to the configure screen
  845. }
  846. /**
  847. * @brief Navigation callback to handle returning to the Wifi Settings screen.
  848. * @param context The context - WebCrawlerApp object.
  849. * @return WebCrawlerViewSubmenu
  850. */
  851. uint32_t web_crawler_back_to_main_callback(void *context)
  852. {
  853. WebCrawlerApp *app = (WebCrawlerApp *)context;
  854. furi_check(app, "web_crawler_back_to_main_callback: WebCrawlerApp is NULL");
  855. return WebCrawlerViewSubmenuMain; // Return to the main submenu
  856. }
  857. static uint32_t web_crawler_back_to_file_callback(void *context)
  858. {
  859. UNUSED(context);
  860. return WebCrawlerViewVariableItemList; // Return to the file submenu
  861. }
  862. uint32_t web_crawler_back_to_wifi_callback(void *context)
  863. {
  864. UNUSED(context);
  865. return WebCrawlerViewVariableItemList; // Return to the wifi submenu
  866. }
  867. uint32_t web_crawler_back_to_request_callback(void *context)
  868. {
  869. UNUSED(context);
  870. return WebCrawlerViewVariableItemList; // Return to the request submenu
  871. }
  872. /**
  873. * @brief Navigation callback to handle exiting the app from the main submenu.
  874. * @param context The context - unused
  875. * @return VIEW_NONE to exit the app
  876. */
  877. uint32_t web_crawler_exit_app_callback(void *context)
  878. {
  879. UNUSED(context);
  880. return VIEW_NONE;
  881. }
  882. /**
  883. * @brief Handle submenu item selection.
  884. * @param context The context - WebCrawlerApp object.
  885. * @param index The WebCrawlerSubmenuIndex item that was clicked.
  886. */
  887. void web_crawler_submenu_callback(void *context, uint32_t index)
  888. {
  889. WebCrawlerApp *app = (WebCrawlerApp *)context;
  890. furi_check(app, "WebCrawlerApp is NULL");
  891. if (app->view_dispatcher)
  892. {
  893. switch (index)
  894. {
  895. case WebCrawlerSubmenuIndexRun:
  896. web_crawler_data_switch_to_view(app);
  897. break;
  898. case WebCrawlerSubmenuIndexAbout:
  899. free_all(app);
  900. if (!alloc_widget(app, WebCrawlerViewAbout))
  901. {
  902. FURI_LOG_E(TAG, "Failed to allocate widget");
  903. return;
  904. }
  905. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewWidget);
  906. break;
  907. case WebCrawlerSubmenuIndexConfig:
  908. free_all(app);
  909. if (!alloc_submenu_config(app))
  910. {
  911. FURI_LOG_E(TAG, "Failed to allocate submenu");
  912. return;
  913. }
  914. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewSubmenuConfig);
  915. break;
  916. case WebCrawlerSubmenuIndexWifi:
  917. free_variable_item_list(app);
  918. if (!alloc_variable_item_list(app, WebCrawlerViewVariableItemListWifi))
  919. {
  920. FURI_LOG_E(TAG, "Failed to allocate variable item list");
  921. return;
  922. }
  923. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewVariableItemList);
  924. break;
  925. case WebCrawlerSubmenuIndexRequest:
  926. free_variable_item_list(app);
  927. if (!alloc_variable_item_list(app, WebCrawlerViewVariableItemListRequest))
  928. {
  929. FURI_LOG_E(TAG, "Failed to allocate variable item list");
  930. return;
  931. }
  932. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewVariableItemList);
  933. break;
  934. case WebCrawlerSubmenuIndexFile:
  935. free_variable_item_list(app);
  936. if (!alloc_variable_item_list(app, WebCrawlerViewVariableItemListFile))
  937. {
  938. FURI_LOG_E(TAG, "Failed to allocate variable item list");
  939. return;
  940. }
  941. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewVariableItemList);
  942. break;
  943. default:
  944. FURI_LOG_E(TAG, "Unknown submenu index");
  945. break;
  946. }
  947. }
  948. }
  949. /**
  950. * @brief Configuration enter callback to handle different items.
  951. * @param context The context - WebCrawlerApp object.
  952. * @param index The index of the item that was clicked.
  953. */
  954. void web_crawler_wifi_enter_callback(void *context, uint32_t index)
  955. {
  956. WebCrawlerApp *app = (WebCrawlerApp *)context;
  957. furi_check(app, "web_crawler_wifi_enter_callback: WebCrawlerApp is NULL");
  958. switch (index)
  959. {
  960. case 0: // SSID
  961. free_text_input(app);
  962. if (!alloc_text_input(app, WebCrawlerViewTextInputSSID))
  963. {
  964. FURI_LOG_E(TAG, "Failed to allocate text input");
  965. return;
  966. }
  967. web_crawler_setting_item_ssid_clicked(context, index);
  968. break;
  969. case 1: // Password
  970. free_text_input(app);
  971. if (!alloc_text_input(app, WebCrawlerViewTextInputPassword))
  972. {
  973. FURI_LOG_E(TAG, "Failed to allocate text input");
  974. return;
  975. }
  976. web_crawler_setting_item_password_clicked(context, index);
  977. break;
  978. default:
  979. FURI_LOG_E(TAG, "Unknown configuration item index");
  980. break;
  981. }
  982. }
  983. /**
  984. * @brief Configuration enter callback to handle different items.
  985. * @param context The context - WebCrawlerApp object.
  986. * @param index The index of the item that was clicked.
  987. */
  988. void web_crawler_file_enter_callback(void *context, uint32_t index)
  989. {
  990. WebCrawlerApp *app = (WebCrawlerApp *)context;
  991. furi_check(app, "web_crawler_file_enter_callback: WebCrawlerApp is NULL");
  992. switch (index)
  993. {
  994. case 0: // File Read
  995. web_crawler_setting_item_file_read_clicked(context, index);
  996. break;
  997. case 1: // FIle Type
  998. free_text_input(app);
  999. if (!alloc_text_input(app, WebCrawlerViewTextInputFileType))
  1000. {
  1001. FURI_LOG_E(TAG, "Failed to allocate text input");
  1002. return;
  1003. }
  1004. web_crawler_setting_item_file_type_clicked(context, index);
  1005. break;
  1006. case 2: // File Rename
  1007. free_text_input(app);
  1008. if (!alloc_text_input(app, WebCrawlerViewTextInputFileRename))
  1009. {
  1010. FURI_LOG_E(TAG, "Failed to allocate text input");
  1011. return;
  1012. }
  1013. web_crawler_setting_item_file_rename_clicked(context, index);
  1014. break;
  1015. case 3: // File Delete
  1016. web_crawler_setting_item_file_delete_clicked(context, index);
  1017. break;
  1018. default:
  1019. FURI_LOG_E(TAG, "Unknown configuration item index");
  1020. break;
  1021. }
  1022. }
  1023. /**
  1024. * @brief Configuration enter callback to handle different items.
  1025. * @param context The context - WebCrawlerApp object.
  1026. * @param index The index of the item that was clicked.
  1027. */
  1028. void web_crawler_request_enter_callback(void *context, uint32_t index)
  1029. {
  1030. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1031. furi_check(app, "web_crawler_request_enter_callback: WebCrawlerApp is NULL");
  1032. switch (index)
  1033. {
  1034. case 0: // URL
  1035. free_text_input(app);
  1036. if (!alloc_text_input(app, WebCrawlerViewTextInput))
  1037. {
  1038. FURI_LOG_E(TAG, "Failed to allocate text input");
  1039. return;
  1040. }
  1041. web_crawler_setting_item_path_clicked(context, index);
  1042. break;
  1043. case 1:
  1044. // HTTP Method
  1045. break;
  1046. case 2:
  1047. // Headers
  1048. free_text_input(app);
  1049. if (!alloc_text_input(app, WebCrawlerViewTextInputHeaders))
  1050. {
  1051. FURI_LOG_E(TAG, "Failed to allocate text input");
  1052. return;
  1053. }
  1054. web_crawler_setting_item_headers_clicked(context, index);
  1055. break;
  1056. case 3:
  1057. // Payload
  1058. free_text_input(app);
  1059. if (!alloc_text_input(app, WebCrawlerViewTextInputPayload))
  1060. {
  1061. FURI_LOG_E(TAG, "Failed to allocate text input");
  1062. return;
  1063. }
  1064. web_crawler_setting_item_payload_clicked(context, index);
  1065. break;
  1066. default:
  1067. FURI_LOG_E(TAG, "Unknown configuration item index");
  1068. break;
  1069. }
  1070. }
  1071. /**
  1072. * @brief Callback for when the user finishes entering the URL.
  1073. * @param context The context - WebCrawlerApp object.
  1074. */
  1075. void web_crawler_set_path_updated(void *context)
  1076. {
  1077. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1078. furi_check(app, "WebCrawlerApp is NULL");
  1079. snprintf(app->path, app->temp_buffer_size_path, "%s", app->temp_buffer_path);
  1080. if (app->path_item)
  1081. {
  1082. variable_item_set_current_value_text(app->path_item, app->path);
  1083. }
  1084. save_char("path", app->temp_buffer_path);
  1085. save_simply();
  1086. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewVariableItemList);
  1087. }
  1088. /**
  1089. * @brief Callback for when the user finishes entering the headers
  1090. * @param context The context - WebCrawlerApp object.
  1091. */
  1092. void web_crawler_set_headers_updated(void *context)
  1093. {
  1094. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1095. furi_check(app, "WebCrawlerApp is NULL");
  1096. snprintf(app->headers, app->temp_buffer_size_headers, "%s", app->temp_buffer_headers);
  1097. if (app->headers_item)
  1098. {
  1099. variable_item_set_current_value_text(app->headers_item, app->headers);
  1100. }
  1101. save_char("headers", app->temp_buffer_headers);
  1102. save_simply();
  1103. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewVariableItemList);
  1104. }
  1105. /**
  1106. * @brief Callback for when the user finishes entering the payload.
  1107. * @param context The context - WebCrawlerApp object.
  1108. */
  1109. void web_crawler_set_payload_updated(void *context)
  1110. {
  1111. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1112. furi_check(app, "WebCrawlerApp is NULL");
  1113. snprintf(app->payload, app->temp_buffer_size_payload, "%s", app->temp_buffer_payload);
  1114. if (app->payload_item)
  1115. {
  1116. variable_item_set_current_value_text(app->payload_item, app->payload);
  1117. }
  1118. save_char("payload", app->temp_buffer_payload);
  1119. save_simply();
  1120. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewVariableItemList);
  1121. }
  1122. /**
  1123. * @brief Callback for when the user finishes entering the SSID.
  1124. * @param context The context - WebCrawlerApp object.
  1125. */
  1126. void web_crawler_set_ssid_updated(void *context)
  1127. {
  1128. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1129. furi_check(app, "WebCrawlerApp is NULL");
  1130. FlipperHTTP *fhttp = flipper_http_alloc();
  1131. furi_check(fhttp, "FlipperHTTP is NULL");
  1132. snprintf(app->ssid, app->temp_buffer_size_ssid, "%s", app->temp_buffer_ssid);
  1133. if (app->ssid_item)
  1134. {
  1135. variable_item_set_current_value_text(app->ssid_item, app->ssid);
  1136. // send to UART
  1137. if (!flipper_http_save_wifi(fhttp, app->ssid, app->password))
  1138. {
  1139. FURI_LOG_E(TAG, "Failed to save wifi settings via UART");
  1140. FURI_LOG_E(TAG, "Make sure the Flipper is connected to the Wifi Dev Board");
  1141. }
  1142. }
  1143. save_char("ssid", app->temp_buffer_ssid);
  1144. save_simply();
  1145. flipper_http_free(fhttp);
  1146. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewVariableItemList);
  1147. }
  1148. /**
  1149. * @brief Callback for when the user finishes entering the Password.
  1150. * @param context The context - WebCrawlerApp object.
  1151. */
  1152. void web_crawler_set_password_update(void *context)
  1153. {
  1154. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1155. furi_check(app, "WebCrawlerApp is NULL");
  1156. FlipperHTTP *fhttp = flipper_http_alloc();
  1157. furi_check(fhttp, "FlipperHTTP is NULL");
  1158. snprintf(app->password, app->temp_buffer_size_password, "%s", app->temp_buffer_password);
  1159. if (app->password_item)
  1160. {
  1161. variable_item_set_current_value_text(app->password_item, app->password);
  1162. // send to UART
  1163. if (!flipper_http_save_wifi(fhttp, app->ssid, app->password))
  1164. {
  1165. FURI_LOG_E(TAG, "Failed to save wifi settings via UART");
  1166. FURI_LOG_E(TAG, "Make sure the Flipper is connected to the Wifi Dev Board");
  1167. }
  1168. }
  1169. save_char("password", app->temp_buffer_password);
  1170. save_simply();
  1171. flipper_http_free(fhttp);
  1172. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewVariableItemList);
  1173. }
  1174. /**
  1175. * @brief Callback for when the user finishes entering the File Type.
  1176. * @param context The context - WebCrawlerApp object.
  1177. */
  1178. void web_crawler_set_file_type_update(void *context)
  1179. {
  1180. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1181. furi_check(app, "WebCrawlerApp is NULL");
  1182. char old_file_type[16];
  1183. snprintf(old_file_type, sizeof(old_file_type), "%s", app->file_type);
  1184. snprintf(app->file_type, app->temp_buffer_size_file_type, "%s", app->temp_buffer_file_type);
  1185. if (app->file_type_item)
  1186. {
  1187. variable_item_set_current_value_text(app->file_type_item, app->file_type);
  1188. }
  1189. char file_rename[128];
  1190. if (load_char("file_rename", file_rename, 128))
  1191. {
  1192. rename_received_data(file_rename, file_rename, app->file_type, old_file_type);
  1193. }
  1194. save_char("file_type", app->file_type);
  1195. save_simply();
  1196. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewVariableItemList);
  1197. }
  1198. /**
  1199. * @brief Callback for when the user finishes entering the File Rename.
  1200. * @param context The context - WebCrawlerApp object.
  1201. */
  1202. void web_crawler_set_file_rename_update(void *context)
  1203. {
  1204. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1205. furi_check(app, "WebCrawlerApp is NULL");
  1206. char old_name[256];
  1207. snprintf(old_name, sizeof(old_name), "%s", app->file_rename);
  1208. snprintf(app->file_rename, app->temp_buffer_size_file_rename, "%s", app->temp_buffer_file_rename);
  1209. if (app->file_rename_item)
  1210. {
  1211. variable_item_set_current_value_text(app->file_rename_item, app->file_rename);
  1212. }
  1213. rename_received_data(old_name, app->file_rename, app->file_type, app->file_type);
  1214. save_char("file_rename", app->file_rename);
  1215. save_simply();
  1216. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewVariableItemList);
  1217. }
  1218. /**
  1219. * @brief Handler for Path configuration item click.
  1220. * @param context The context - WebCrawlerApp object.
  1221. * @param index The index of the item that was clicked.
  1222. */
  1223. void web_crawler_setting_item_path_clicked(void *context, uint32_t index)
  1224. {
  1225. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1226. furi_check(app, "WebCrawlerApp is NULL");
  1227. if (!app->uart_text_input)
  1228. {
  1229. FURI_LOG_E(TAG, "Text input is NULL");
  1230. return;
  1231. }
  1232. UNUSED(index);
  1233. // Initialize temp_buffer with existing path
  1234. if (app->path && strlen(app->path) > 0)
  1235. {
  1236. strncpy(app->temp_buffer_path, app->path, app->temp_buffer_size_path - 1);
  1237. }
  1238. else
  1239. {
  1240. strncpy(app->temp_buffer_path, "https://httpbin.org/get", app->temp_buffer_size_path - 1);
  1241. }
  1242. app->temp_buffer_path[app->temp_buffer_size_path - 1] = '\0';
  1243. // Configure the text input
  1244. bool clear_previous_text = false;
  1245. uart_text_input_set_result_callback(
  1246. app->uart_text_input,
  1247. web_crawler_set_path_updated,
  1248. app,
  1249. app->temp_buffer_path,
  1250. app->temp_buffer_size_path,
  1251. clear_previous_text);
  1252. // Set the previous callback to return to Configure screen
  1253. view_set_previous_callback(
  1254. uart_text_input_get_view(app->uart_text_input),
  1255. web_crawler_back_to_request_callback);
  1256. // Show text input dialog
  1257. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewInput);
  1258. }
  1259. /**
  1260. * @brief Handler for headers configuration item click.
  1261. * @param context The context - WebCrawlerApp object.
  1262. * @param index The index of the item that was clicked.
  1263. */
  1264. void web_crawler_setting_item_headers_clicked(void *context, uint32_t index)
  1265. {
  1266. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1267. furi_check(app, "WebCrawlerApp is NULL");
  1268. UNUSED(index);
  1269. if (!app->uart_text_input)
  1270. {
  1271. FURI_LOG_E(TAG, "Text input is NULL");
  1272. return;
  1273. }
  1274. if (!app->headers)
  1275. {
  1276. FURI_LOG_E(TAG, "Headers is NULL");
  1277. return;
  1278. }
  1279. if (!app->temp_buffer_headers)
  1280. {
  1281. FURI_LOG_E(TAG, "Temp buffer headers is NULL");
  1282. return;
  1283. }
  1284. // Initialize temp_buffer with existing headers
  1285. if (app->headers && strlen(app->headers) > 0)
  1286. {
  1287. strncpy(app->temp_buffer_headers, app->headers, app->temp_buffer_size_headers - 1);
  1288. }
  1289. else
  1290. {
  1291. strncpy(app->temp_buffer_headers, "{\"Content-Type\":\"application/json\",\"key\":\"value\"}", app->temp_buffer_size_headers - 1);
  1292. }
  1293. app->temp_buffer_headers[app->temp_buffer_size_headers - 1] = '\0';
  1294. // Configure the text input
  1295. bool clear_previous_text = false;
  1296. uart_text_input_set_result_callback(
  1297. app->uart_text_input,
  1298. web_crawler_set_headers_updated,
  1299. app,
  1300. app->temp_buffer_headers,
  1301. app->temp_buffer_size_headers,
  1302. clear_previous_text);
  1303. // Set the previous callback to return to Configure screen
  1304. view_set_previous_callback(
  1305. uart_text_input_get_view(app->uart_text_input),
  1306. web_crawler_back_to_request_callback);
  1307. // Show text input dialog
  1308. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewInput);
  1309. }
  1310. /**
  1311. * @brief Handler for payload configuration item click.
  1312. * @param context The context - WebCrawlerApp object.
  1313. * @param index The index of the item that was clicked.
  1314. */
  1315. void web_crawler_setting_item_payload_clicked(void *context, uint32_t index)
  1316. {
  1317. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1318. furi_check(app, "WebCrawlerApp is NULL");
  1319. UNUSED(index);
  1320. if (!app->uart_text_input)
  1321. {
  1322. FURI_LOG_E(TAG, "Text input is NULL");
  1323. return;
  1324. }
  1325. // Initialize temp_buffer with existing payload
  1326. if (app->payload && strlen(app->payload) > 0)
  1327. {
  1328. strncpy(app->temp_buffer_payload, app->payload, app->temp_buffer_size_payload - 1);
  1329. }
  1330. else
  1331. {
  1332. strncpy(app->temp_buffer_payload, "{\"key\":\"value\"}", app->temp_buffer_size_payload - 1);
  1333. }
  1334. app->temp_buffer_payload[app->temp_buffer_size_payload - 1] = '\0';
  1335. // Configure the text input
  1336. bool clear_previous_text = false;
  1337. uart_text_input_set_result_callback(
  1338. app->uart_text_input,
  1339. web_crawler_set_payload_updated,
  1340. app,
  1341. app->temp_buffer_payload,
  1342. app->temp_buffer_size_payload,
  1343. clear_previous_text);
  1344. // Set the previous callback to return to Configure screen
  1345. view_set_previous_callback(
  1346. uart_text_input_get_view(app->uart_text_input),
  1347. web_crawler_back_to_request_callback);
  1348. // Show text input dialog
  1349. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewInput);
  1350. }
  1351. /**
  1352. * @brief Handler for SSID configuration item click.
  1353. * @param context The context - WebCrawlerApp object.
  1354. * @param index The index of the item that was clicked.
  1355. */
  1356. void web_crawler_setting_item_ssid_clicked(void *context, uint32_t index)
  1357. {
  1358. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1359. furi_check(app, "WebCrawlerApp is NULL");
  1360. UNUSED(index);
  1361. if (!app->uart_text_input)
  1362. {
  1363. FURI_LOG_E(TAG, "Text input is NULL");
  1364. return;
  1365. }
  1366. // Initialize temp_buffer with existing SSID
  1367. if (app->ssid && strlen(app->ssid) > 0)
  1368. {
  1369. strncpy(app->temp_buffer_ssid, app->ssid, app->temp_buffer_size_ssid - 1);
  1370. }
  1371. else
  1372. {
  1373. strncpy(app->temp_buffer_ssid, "", app->temp_buffer_size_ssid - 1);
  1374. }
  1375. app->temp_buffer_ssid[app->temp_buffer_size_ssid - 1] = '\0';
  1376. // Configure the text input
  1377. bool clear_previous_text = false;
  1378. uart_text_input_set_result_callback(
  1379. app->uart_text_input,
  1380. web_crawler_set_ssid_updated,
  1381. app,
  1382. app->temp_buffer_ssid,
  1383. app->temp_buffer_size_ssid,
  1384. clear_previous_text);
  1385. // Set the previous callback to return to Configure screen
  1386. view_set_previous_callback(
  1387. uart_text_input_get_view(app->uart_text_input),
  1388. web_crawler_back_to_wifi_callback);
  1389. // Show text input dialog
  1390. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewInput);
  1391. }
  1392. /**
  1393. * @brief Handler for Password configuration item click.
  1394. * @param context The context - WebCrawlerApp object.
  1395. * @param index The index of the item that was clicked.
  1396. */
  1397. void web_crawler_setting_item_password_clicked(void *context, uint32_t index)
  1398. {
  1399. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1400. furi_check(app, "WebCrawlerApp is NULL");
  1401. UNUSED(index);
  1402. if (!app->uart_text_input)
  1403. {
  1404. FURI_LOG_E(TAG, "Text input is NULL");
  1405. return;
  1406. }
  1407. // Initialize temp_buffer with existing password
  1408. strncpy(app->temp_buffer_password, app->password, app->temp_buffer_size_password - 1);
  1409. app->temp_buffer_password[app->temp_buffer_size_password - 1] = '\0';
  1410. // Configure the text input
  1411. bool clear_previous_text = false;
  1412. uart_text_input_set_result_callback(
  1413. app->uart_text_input,
  1414. web_crawler_set_password_update,
  1415. app,
  1416. app->temp_buffer_password,
  1417. app->temp_buffer_size_password,
  1418. clear_previous_text);
  1419. // Set the previous callback to return to Configure screen
  1420. view_set_previous_callback(
  1421. uart_text_input_get_view(app->uart_text_input),
  1422. web_crawler_back_to_wifi_callback);
  1423. // Show text input dialog
  1424. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewInput);
  1425. }
  1426. /**
  1427. * @brief Handler for File Type configuration item click.
  1428. * @param context The context - WebCrawlerApp object.
  1429. * @param index The index of the item that was clicked.
  1430. */
  1431. void web_crawler_setting_item_file_type_clicked(void *context, uint32_t index)
  1432. {
  1433. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1434. furi_check(app, "WebCrawlerApp is NULL");
  1435. UNUSED(index);
  1436. if (!app->uart_text_input)
  1437. {
  1438. FURI_LOG_E(TAG, "Text input is NULL");
  1439. return;
  1440. }
  1441. // Initialize temp_buffer with existing file_type
  1442. if (app->file_type && strlen(app->file_type) > 0)
  1443. {
  1444. strncpy(app->temp_buffer_file_type, app->file_type, app->temp_buffer_size_file_type - 1);
  1445. }
  1446. else
  1447. {
  1448. strncpy(app->temp_buffer_file_type, ".txt", app->temp_buffer_size_file_type - 1);
  1449. }
  1450. app->temp_buffer_file_type[app->temp_buffer_size_file_type - 1] = '\0';
  1451. // Configure the text input
  1452. bool clear_previous_text = false;
  1453. uart_text_input_set_result_callback(
  1454. app->uart_text_input,
  1455. web_crawler_set_file_type_update,
  1456. app,
  1457. app->temp_buffer_file_type,
  1458. app->temp_buffer_size_file_type,
  1459. clear_previous_text);
  1460. // Set the previous callback to return to Configure screen
  1461. view_set_previous_callback(
  1462. uart_text_input_get_view(app->uart_text_input),
  1463. web_crawler_back_to_file_callback);
  1464. // Show text input dialog
  1465. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewInput);
  1466. }
  1467. /**
  1468. * @brief Handler for File Rename configuration item click.
  1469. * @param context The context - WebCrawlerApp object.
  1470. * @param index The index of the item that was clicked.
  1471. */
  1472. void web_crawler_setting_item_file_rename_clicked(void *context, uint32_t index)
  1473. {
  1474. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1475. furi_check(app, "WebCrawlerApp is NULL");
  1476. UNUSED(index);
  1477. if (!app->uart_text_input)
  1478. {
  1479. FURI_LOG_E(TAG, "Text input is NULL");
  1480. return;
  1481. }
  1482. // Initialize temp_buffer with existing file_rename
  1483. if (app->file_rename && strlen(app->file_rename) > 0)
  1484. {
  1485. strncpy(app->temp_buffer_file_rename, app->file_rename, app->temp_buffer_size_file_rename - 1);
  1486. }
  1487. else
  1488. {
  1489. strncpy(app->temp_buffer_file_rename, "received_data", app->temp_buffer_size_file_rename - 1);
  1490. }
  1491. app->temp_buffer_file_rename[app->temp_buffer_size_file_rename - 1] = '\0';
  1492. // Configure the text input
  1493. bool clear_previous_text = false;
  1494. uart_text_input_set_result_callback(
  1495. app->uart_text_input,
  1496. web_crawler_set_file_rename_update,
  1497. app,
  1498. app->temp_buffer_file_rename,
  1499. app->temp_buffer_size_file_rename,
  1500. clear_previous_text);
  1501. // Set the previous callback to return to Configure screen
  1502. view_set_previous_callback(
  1503. uart_text_input_get_view(app->uart_text_input),
  1504. web_crawler_back_to_file_callback);
  1505. // Show text input dialog
  1506. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewInput);
  1507. }
  1508. /**
  1509. * @brief Handler for File Delete configuration item click.
  1510. * @param context The context - WebCrawlerApp object.
  1511. * @param index The index of the item that was clicked.
  1512. */
  1513. void web_crawler_setting_item_file_delete_clicked(void *context, uint32_t index)
  1514. {
  1515. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1516. furi_check(app, "WebCrawlerApp is NULL");
  1517. UNUSED(index);
  1518. free_widget(app);
  1519. if (!alloc_widget(app, WebCrawlerViewFileDelete))
  1520. {
  1521. FURI_LOG_E(TAG, "web_crawler_setting_item_file_delete_clicked: Failed to allocate widget");
  1522. return;
  1523. }
  1524. if (!delete_received_data())
  1525. {
  1526. FURI_LOG_E(TAG, "Failed to delete file");
  1527. }
  1528. // Set the previous callback to return to Configure screen
  1529. view_set_previous_callback(
  1530. widget_get_view(app->widget),
  1531. web_crawler_back_to_file_callback);
  1532. // Show text input dialog
  1533. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewWidget);
  1534. }
  1535. void web_crawler_setting_item_file_read_clicked(void *context, uint32_t index)
  1536. {
  1537. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1538. furi_check(app, "WebCrawlerApp is NULL");
  1539. UNUSED(index);
  1540. free_widget(app);
  1541. if (!alloc_widget(app, WebCrawlerViewFileRead))
  1542. {
  1543. FURI_LOG_E(TAG, "web_crawler_setting_item_file_read_clicked: Failed to allocate widget");
  1544. return;
  1545. }
  1546. widget_reset(app->widget);
  1547. char file_path[256];
  1548. char file_rename[128];
  1549. char file_type[16];
  1550. if (load_char("file_rename", file_rename, 128) && load_char("file_type", file_type, 16))
  1551. {
  1552. snprintf(file_path, sizeof(file_path), "%s%s%s", RECEIVED_DATA_PATH, file_rename, file_type);
  1553. }
  1554. else
  1555. {
  1556. snprintf(file_path, sizeof(file_path), "%s%s%s", RECEIVED_DATA_PATH, "received_data", ".txt");
  1557. }
  1558. // load the received data from the saved file
  1559. FuriString *received_data = flipper_http_load_from_file(file_path);
  1560. if (received_data == NULL)
  1561. {
  1562. FURI_LOG_E(TAG, "Failed to load received data from file.");
  1563. if (app->widget)
  1564. {
  1565. widget_add_text_scroll_element(
  1566. app->widget,
  1567. 0,
  1568. 0,
  1569. 128,
  1570. 64, "File is empty.");
  1571. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewWidget);
  1572. }
  1573. return;
  1574. }
  1575. widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, furi_string_get_cstr(received_data));
  1576. furi_string_free(received_data);
  1577. // Set the previous callback to return to Configure screen
  1578. view_set_previous_callback(
  1579. widget_get_view(app->widget),
  1580. web_crawler_back_to_file_callback);
  1581. // Show text input dialog
  1582. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewWidget);
  1583. }
  1584. static void web_crawler_widget_set_text(char *message, Widget **widget)
  1585. {
  1586. if (widget == NULL)
  1587. {
  1588. FURI_LOG_E(TAG, "flip_weather_set_widget_text - widget is NULL");
  1589. DEV_CRASH();
  1590. return;
  1591. }
  1592. if (message == NULL)
  1593. {
  1594. FURI_LOG_E(TAG, "flip_weather_set_widget_text - message is NULL");
  1595. DEV_CRASH();
  1596. return;
  1597. }
  1598. widget_reset(*widget);
  1599. uint32_t message_length = strlen(message); // Length of the message
  1600. uint32_t i = 0; // Index tracker
  1601. uint32_t formatted_index = 0; // Tracker for where we are in the formatted message
  1602. char *formatted_message; // Buffer to hold the final formatted message
  1603. // Allocate buffer with double the message length plus one for safety
  1604. if (!easy_flipper_set_buffer(&formatted_message, message_length * 2 + 1))
  1605. {
  1606. return;
  1607. }
  1608. while (i < message_length)
  1609. {
  1610. uint32_t max_line_length = 31; // Maximum characters per line
  1611. uint32_t remaining_length = message_length - i; // Remaining characters
  1612. uint32_t line_length = (remaining_length < max_line_length) ? remaining_length : max_line_length;
  1613. // Check for newline character within the current segment
  1614. uint32_t newline_pos = i;
  1615. bool found_newline = false;
  1616. for (; newline_pos < i + line_length && newline_pos < message_length; newline_pos++)
  1617. {
  1618. if (message[newline_pos] == '\n')
  1619. {
  1620. found_newline = true;
  1621. break;
  1622. }
  1623. }
  1624. if (found_newline)
  1625. {
  1626. // If newline found, set line_length up to the newline
  1627. line_length = newline_pos - i;
  1628. }
  1629. // Temporary buffer to hold the current line
  1630. char line[32];
  1631. strncpy(line, message + i, line_length);
  1632. line[line_length] = '\0';
  1633. // If newline was found, skip it for the next iteration
  1634. if (found_newline)
  1635. {
  1636. i += line_length + 1; // +1 to skip the '\n' character
  1637. }
  1638. else
  1639. {
  1640. // Check if the line ends in the middle of a word and adjust accordingly
  1641. if (line_length == max_line_length && message[i + line_length] != '\0' && message[i + line_length] != ' ')
  1642. {
  1643. // Find the last space within the current line to avoid breaking a word
  1644. char *last_space = strrchr(line, ' ');
  1645. if (last_space != NULL)
  1646. {
  1647. // Adjust the line_length to avoid cutting the word
  1648. line_length = last_space - line;
  1649. line[line_length] = '\0'; // Null-terminate at the space
  1650. }
  1651. }
  1652. // Move the index forward by the determined line_length
  1653. i += line_length;
  1654. // Skip any spaces at the beginning of the next line
  1655. while (i < message_length && message[i] == ' ')
  1656. {
  1657. i++;
  1658. }
  1659. }
  1660. // Manually copy the fixed line into the formatted_message buffer
  1661. for (uint32_t j = 0; j < line_length; j++)
  1662. {
  1663. formatted_message[formatted_index++] = line[j];
  1664. }
  1665. // Add a newline character for line spacing
  1666. formatted_message[formatted_index++] = '\n';
  1667. }
  1668. // Null-terminate the formatted_message
  1669. formatted_message[formatted_index] = '\0';
  1670. // Add the formatted message to the widget
  1671. widget_add_text_scroll_element(*widget, 0, 0, 128, 64, formatted_message);
  1672. }
  1673. void web_crawler_loader_draw_callback(Canvas *canvas, void *model)
  1674. {
  1675. if (!canvas || !model)
  1676. {
  1677. FURI_LOG_E(TAG, "web_crawler_loader_draw_callback - canvas or model is NULL");
  1678. return;
  1679. }
  1680. DataLoaderModel *data_loader_model = (DataLoaderModel *)model;
  1681. SerialState http_state = data_loader_model->fhttp->state;
  1682. DataState data_state = data_loader_model->data_state;
  1683. char *title = data_loader_model->title;
  1684. canvas_set_font(canvas, FontSecondary);
  1685. if (http_state == INACTIVE)
  1686. {
  1687. canvas_draw_str(canvas, 0, 7, "Wifi Dev Board disconnected.");
  1688. canvas_draw_str(canvas, 0, 17, "Please connect to the board.");
  1689. canvas_draw_str(canvas, 0, 32, "If your board is connected,");
  1690. canvas_draw_str(canvas, 0, 42, "make sure you have flashed");
  1691. canvas_draw_str(canvas, 0, 52, "your WiFi Devboard with the");
  1692. canvas_draw_str(canvas, 0, 62, "latest FlipperHTTP flash.");
  1693. return;
  1694. }
  1695. if (data_state == DataStateError || data_state == DataStateParseError)
  1696. {
  1697. web_crawler_draw_error(canvas, data_loader_model);
  1698. return;
  1699. }
  1700. canvas_draw_str(canvas, 0, 7, title);
  1701. canvas_draw_str(canvas, 0, 17, "Loading...");
  1702. if (data_state == DataStateInitial)
  1703. {
  1704. return;
  1705. }
  1706. if (http_state == SENDING)
  1707. {
  1708. canvas_draw_str(canvas, 0, 27, "Fetching...");
  1709. return;
  1710. }
  1711. if (http_state == RECEIVING || data_state == DataStateRequested)
  1712. {
  1713. canvas_draw_str(canvas, 0, 27, "Receiving...");
  1714. return;
  1715. }
  1716. if (http_state == IDLE && data_state == DataStateReceived)
  1717. {
  1718. canvas_draw_str(canvas, 0, 27, "Processing...");
  1719. return;
  1720. }
  1721. if (http_state == IDLE && data_state == DataStateParsed)
  1722. {
  1723. canvas_draw_str(canvas, 0, 27, "Processed...");
  1724. return;
  1725. }
  1726. }
  1727. static void web_crawler_loader_process_callback(void *context)
  1728. {
  1729. if (context == NULL)
  1730. {
  1731. FURI_LOG_E(TAG, "web_crawler_loader_process_callback - context is NULL");
  1732. DEV_CRASH();
  1733. return;
  1734. }
  1735. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1736. View *view = app->view_loader;
  1737. DataState current_data_state;
  1738. DataLoaderModel *loader_model = NULL;
  1739. with_view_model(view, DataLoaderModel * model, { current_data_state = model->data_state; loader_model = model; }, false);
  1740. if (!loader_model || !loader_model->fhttp)
  1741. {
  1742. FURI_LOG_E(TAG, "Model or fhttp is NULL");
  1743. DEV_CRASH();
  1744. return;
  1745. }
  1746. if (current_data_state == DataStateInitial)
  1747. {
  1748. with_view_model(
  1749. view,
  1750. DataLoaderModel * model,
  1751. {
  1752. model->data_state = DataStateRequested;
  1753. DataLoaderFetch fetch = model->fetcher;
  1754. if (fetch == NULL)
  1755. {
  1756. FURI_LOG_E(TAG, "Model doesn't have Fetch function assigned.");
  1757. model->data_state = DataStateError;
  1758. return;
  1759. }
  1760. // Clear any previous responses
  1761. strncpy(model->fhttp->last_response, "", 1);
  1762. bool request_status = fetch(model);
  1763. if (!request_status)
  1764. {
  1765. model->data_state = DataStateError;
  1766. }
  1767. },
  1768. true);
  1769. }
  1770. else if (current_data_state == DataStateRequested || current_data_state == DataStateError)
  1771. {
  1772. if (loader_model->fhttp->state == IDLE && loader_model->fhttp->last_response != NULL)
  1773. {
  1774. if (strstr(loader_model->fhttp->last_response, "[PONG]") != NULL)
  1775. {
  1776. FURI_LOG_DEV(TAG, "PONG received.");
  1777. }
  1778. else if (strncmp(loader_model->fhttp->last_response, "[SUCCESS]", 9) == 0)
  1779. {
  1780. FURI_LOG_DEV(TAG, "SUCCESS received. %s", loader_model->fhttp->last_response ? loader_model->fhttp->last_response : "NULL");
  1781. }
  1782. else if (strncmp(loader_model->fhttp->last_response, "[ERROR]", 9) == 0)
  1783. {
  1784. FURI_LOG_DEV(TAG, "ERROR received. %s", loader_model->fhttp->last_response ? loader_model->fhttp->last_response : "NULL");
  1785. }
  1786. else if (strlen(loader_model->fhttp->last_response) == 0)
  1787. {
  1788. // Still waiting on response
  1789. }
  1790. else
  1791. {
  1792. with_view_model(view, DataLoaderModel * model, { model->data_state = DataStateReceived; }, true);
  1793. }
  1794. }
  1795. else if (loader_model->fhttp->state == SENDING || loader_model->fhttp->state == RECEIVING)
  1796. {
  1797. // continue waiting
  1798. }
  1799. else if (loader_model->fhttp->state == INACTIVE)
  1800. {
  1801. // inactive. try again
  1802. }
  1803. else if (loader_model->fhttp->state == ISSUE)
  1804. {
  1805. with_view_model(view, DataLoaderModel * model, { model->data_state = DataStateError; }, true);
  1806. }
  1807. else
  1808. {
  1809. FURI_LOG_DEV(TAG, "Unexpected state: %d lastresp: %s", loader_model->fhttp->state, loader_model->fhttp->last_response ? loader_model->fhttp->last_response : "NULL");
  1810. DEV_CRASH();
  1811. }
  1812. }
  1813. else if (current_data_state == DataStateReceived)
  1814. {
  1815. with_view_model(
  1816. view,
  1817. DataLoaderModel * model,
  1818. {
  1819. char *data_text;
  1820. if (model->parser == NULL)
  1821. {
  1822. data_text = NULL;
  1823. FURI_LOG_DEV(TAG, "Parser is NULL");
  1824. DEV_CRASH();
  1825. }
  1826. else
  1827. {
  1828. data_text = model->parser(model);
  1829. }
  1830. FURI_LOG_DEV(TAG, "Parsed data: %s\r\ntext: %s", model->fhttp->last_response ? model->fhttp->last_response : "NULL", data_text ? data_text : "NULL");
  1831. model->data_text = data_text;
  1832. if (data_text == NULL)
  1833. {
  1834. model->data_state = DataStateParseError;
  1835. }
  1836. else
  1837. {
  1838. model->data_state = DataStateParsed;
  1839. }
  1840. },
  1841. true);
  1842. }
  1843. else if (current_data_state == DataStateParsed)
  1844. {
  1845. with_view_model(
  1846. view,
  1847. DataLoaderModel * model,
  1848. {
  1849. if (++model->request_index < model->request_count)
  1850. {
  1851. model->data_state = DataStateInitial;
  1852. }
  1853. else
  1854. {
  1855. web_crawler_widget_set_text(model->data_text != NULL ? model->data_text : "", &app->widget_result);
  1856. if (model->data_text != NULL)
  1857. {
  1858. free(model->data_text);
  1859. model->data_text = NULL;
  1860. }
  1861. view_set_previous_callback(widget_get_view(app->widget_result), model->back_callback);
  1862. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewWidgetResult);
  1863. }
  1864. },
  1865. true);
  1866. }
  1867. }
  1868. static void web_crawler_loader_timer_callback(void *context)
  1869. {
  1870. if (context == NULL)
  1871. {
  1872. FURI_LOG_E(TAG, "web_crawler_loader_timer_callback - context is NULL");
  1873. DEV_CRASH();
  1874. return;
  1875. }
  1876. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1877. view_dispatcher_send_custom_event(app->view_dispatcher, WebCrawlerCustomEventProcess);
  1878. }
  1879. static void web_crawler_loader_on_enter(void *context)
  1880. {
  1881. if (context == NULL)
  1882. {
  1883. FURI_LOG_E(TAG, "web_crawler_loader_on_enter - context is NULL");
  1884. DEV_CRASH();
  1885. return;
  1886. }
  1887. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1888. View *view = app->view_loader;
  1889. with_view_model(
  1890. view,
  1891. DataLoaderModel * model,
  1892. {
  1893. view_set_previous_callback(view, model->back_callback);
  1894. if (model->timer == NULL)
  1895. {
  1896. model->timer = furi_timer_alloc(web_crawler_loader_timer_callback, FuriTimerTypePeriodic, app);
  1897. }
  1898. furi_timer_start(model->timer, 250);
  1899. },
  1900. true);
  1901. }
  1902. static void web_crawler_loader_on_exit(void *context)
  1903. {
  1904. if (context == NULL)
  1905. {
  1906. FURI_LOG_E(TAG, "web_crawler_loader_on_exit - context is NULL");
  1907. DEV_CRASH();
  1908. return;
  1909. }
  1910. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1911. View *view = app->view_loader;
  1912. with_view_model(
  1913. view,
  1914. DataLoaderModel * model,
  1915. {
  1916. if (model->timer)
  1917. {
  1918. furi_timer_stop(model->timer);
  1919. }
  1920. },
  1921. false);
  1922. }
  1923. void web_crawler_loader_init(View *view)
  1924. {
  1925. if (view == NULL)
  1926. {
  1927. FURI_LOG_E(TAG, "web_crawler_loader_init - view is NULL");
  1928. DEV_CRASH();
  1929. return;
  1930. }
  1931. view_allocate_model(view, ViewModelTypeLocking, sizeof(DataLoaderModel));
  1932. view_set_enter_callback(view, web_crawler_loader_on_enter);
  1933. view_set_exit_callback(view, web_crawler_loader_on_exit);
  1934. }
  1935. void web_crawler_loader_free_model(View *view)
  1936. {
  1937. if (view == NULL)
  1938. {
  1939. FURI_LOG_E(TAG, "web_crawler_loader_free_model - view is NULL");
  1940. DEV_CRASH();
  1941. return;
  1942. }
  1943. with_view_model(
  1944. view,
  1945. DataLoaderModel * model,
  1946. {
  1947. if (model->timer)
  1948. {
  1949. furi_timer_free(model->timer);
  1950. model->timer = NULL;
  1951. }
  1952. if (model->parser_context)
  1953. {
  1954. // do not free the context here, it is the app context
  1955. // free(model->parser_context);
  1956. // model->parser_context = NULL;
  1957. }
  1958. if (model->fhttp)
  1959. {
  1960. flipper_http_free(model->fhttp);
  1961. model->fhttp = NULL;
  1962. }
  1963. },
  1964. false);
  1965. }
  1966. bool web_crawler_custom_event_callback(void *context, uint32_t index)
  1967. {
  1968. if (context == NULL)
  1969. {
  1970. FURI_LOG_E(TAG, "web_crawler_custom_event_callback - context is NULL");
  1971. DEV_CRASH();
  1972. return false;
  1973. }
  1974. switch (index)
  1975. {
  1976. case WebCrawlerCustomEventProcess:
  1977. web_crawler_loader_process_callback(context);
  1978. return true;
  1979. default:
  1980. FURI_LOG_DEV(TAG, "web_crawler_custom_event_callback. Unknown index: %ld", index);
  1981. return false;
  1982. }
  1983. }
  1984. void web_crawler_generic_switch_to_view(WebCrawlerApp *app, char *title, DataLoaderFetch fetcher, DataLoaderParser parser, size_t request_count, ViewNavigationCallback back, uint32_t view_id)
  1985. {
  1986. if (app == NULL)
  1987. {
  1988. FURI_LOG_E(TAG, "web_crawler_generic_switch_to_view - app is NULL");
  1989. DEV_CRASH();
  1990. return;
  1991. }
  1992. View *view = app->view_loader;
  1993. if (view == NULL)
  1994. {
  1995. FURI_LOG_E(TAG, "web_crawler_generic_switch_to_view - view is NULL");
  1996. DEV_CRASH();
  1997. return;
  1998. }
  1999. with_view_model(
  2000. view,
  2001. DataLoaderModel * model,
  2002. {
  2003. model->title = title;
  2004. model->fetcher = fetcher;
  2005. model->parser = parser;
  2006. model->request_index = 0;
  2007. model->request_count = request_count;
  2008. model->back_callback = back;
  2009. model->data_state = DataStateInitial;
  2010. model->data_text = NULL;
  2011. //
  2012. model->parser_context = app;
  2013. if (!model->fhttp)
  2014. {
  2015. model->fhttp = flipper_http_alloc();
  2016. }
  2017. },
  2018. true);
  2019. view_dispatcher_switch_to_view(app->view_dispatcher, view_id);
  2020. }