web_crawler_callback.c 75 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176
  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. if (!fhttp)
  1132. {
  1133. FURI_LOG_E(TAG, "Failed to allocate FlipperHTTP");
  1134. easy_flipper_dialog("[ERROR]", "Failed to allocate FlipperHTTP\nand save wifi settings");
  1135. return;
  1136. }
  1137. char password[64];
  1138. snprintf(app->ssid, app->temp_buffer_size_ssid, "%s", app->temp_buffer_ssid);
  1139. if (app->ssid_item)
  1140. {
  1141. variable_item_set_current_value_text(app->ssid_item, app->ssid);
  1142. if (load_char("password", password, 64))
  1143. {
  1144. // send to UART
  1145. if (!flipper_http_save_wifi(fhttp, app->ssid, password))
  1146. {
  1147. FURI_LOG_E(TAG, "Failed to save wifi settings via UART");
  1148. FURI_LOG_E(TAG, "Make sure the Flipper is connected to the Wifi Dev Board");
  1149. }
  1150. }
  1151. }
  1152. save_char("ssid", app->temp_buffer_ssid);
  1153. save_simply();
  1154. flipper_http_free(fhttp);
  1155. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewVariableItemList);
  1156. }
  1157. /**
  1158. * @brief Callback for when the user finishes entering the Password.
  1159. * @param context The context - WebCrawlerApp object.
  1160. */
  1161. void web_crawler_set_password_update(void *context)
  1162. {
  1163. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1164. furi_check(app, "WebCrawlerApp is NULL");
  1165. FlipperHTTP *fhttp = flipper_http_alloc();
  1166. if (!fhttp)
  1167. {
  1168. FURI_LOG_E(TAG, "Failed to allocate FlipperHTTP");
  1169. easy_flipper_dialog("[ERROR]", "Failed to allocate FlipperHTTP\nand save wifi settings");
  1170. return;
  1171. }
  1172. char ssid[64];
  1173. snprintf(app->password, app->temp_buffer_size_password, "%s", app->temp_buffer_password);
  1174. if (app->password_item)
  1175. {
  1176. variable_item_set_current_value_text(app->password_item, app->password);
  1177. // send to UART
  1178. if (load_char("ssid", ssid, 64))
  1179. {
  1180. if (!flipper_http_save_wifi(fhttp, ssid, app->password))
  1181. {
  1182. FURI_LOG_E(TAG, "Failed to save wifi settings via UART");
  1183. FURI_LOG_E(TAG, "Make sure the Flipper is connected to the Wifi Dev Board");
  1184. }
  1185. }
  1186. }
  1187. save_char("password", app->temp_buffer_password);
  1188. save_simply();
  1189. flipper_http_free(fhttp);
  1190. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewVariableItemList);
  1191. }
  1192. /**
  1193. * @brief Callback for when the user finishes entering the File Type.
  1194. * @param context The context - WebCrawlerApp object.
  1195. */
  1196. void web_crawler_set_file_type_update(void *context)
  1197. {
  1198. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1199. furi_check(app, "WebCrawlerApp is NULL");
  1200. char old_file_type[16];
  1201. snprintf(old_file_type, sizeof(old_file_type), "%s", app->file_type);
  1202. snprintf(app->file_type, app->temp_buffer_size_file_type, "%s", app->temp_buffer_file_type);
  1203. if (app->file_type_item)
  1204. {
  1205. variable_item_set_current_value_text(app->file_type_item, app->file_type);
  1206. }
  1207. char file_rename[128];
  1208. if (load_char("file_rename", file_rename, 128))
  1209. {
  1210. rename_received_data(file_rename, file_rename, app->file_type, old_file_type);
  1211. }
  1212. save_char("file_type", app->file_type);
  1213. save_simply();
  1214. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewVariableItemList);
  1215. }
  1216. /**
  1217. * @brief Callback for when the user finishes entering the File Rename.
  1218. * @param context The context - WebCrawlerApp object.
  1219. */
  1220. void web_crawler_set_file_rename_update(void *context)
  1221. {
  1222. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1223. furi_check(app, "WebCrawlerApp is NULL");
  1224. char old_name[256];
  1225. snprintf(old_name, sizeof(old_name), "%s", app->file_rename);
  1226. snprintf(app->file_rename, app->temp_buffer_size_file_rename, "%s", app->temp_buffer_file_rename);
  1227. if (app->file_rename_item)
  1228. {
  1229. variable_item_set_current_value_text(app->file_rename_item, app->file_rename);
  1230. }
  1231. rename_received_data(old_name, app->file_rename, app->file_type, app->file_type);
  1232. save_char("file_rename", app->file_rename);
  1233. save_simply();
  1234. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewVariableItemList);
  1235. }
  1236. /**
  1237. * @brief Handler for Path configuration item click.
  1238. * @param context The context - WebCrawlerApp object.
  1239. * @param index The index of the item that was clicked.
  1240. */
  1241. void web_crawler_setting_item_path_clicked(void *context, uint32_t index)
  1242. {
  1243. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1244. furi_check(app, "WebCrawlerApp is NULL");
  1245. if (!app->uart_text_input)
  1246. {
  1247. FURI_LOG_E(TAG, "Text input is NULL");
  1248. return;
  1249. }
  1250. UNUSED(index);
  1251. // Initialize temp_buffer with existing path
  1252. if (app->path && strlen(app->path) > 0)
  1253. {
  1254. strncpy(app->temp_buffer_path, app->path, app->temp_buffer_size_path - 1);
  1255. }
  1256. else
  1257. {
  1258. strncpy(app->temp_buffer_path, "https://httpbin.org/get", app->temp_buffer_size_path - 1);
  1259. }
  1260. app->temp_buffer_path[app->temp_buffer_size_path - 1] = '\0';
  1261. // Configure the text input
  1262. bool clear_previous_text = false;
  1263. uart_text_input_set_result_callback(
  1264. app->uart_text_input,
  1265. web_crawler_set_path_updated,
  1266. app,
  1267. app->temp_buffer_path,
  1268. app->temp_buffer_size_path,
  1269. clear_previous_text);
  1270. // Set the previous callback to return to Configure screen
  1271. view_set_previous_callback(
  1272. uart_text_input_get_view(app->uart_text_input),
  1273. web_crawler_back_to_request_callback);
  1274. // Show text input dialog
  1275. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewInput);
  1276. }
  1277. /**
  1278. * @brief Handler for headers configuration item click.
  1279. * @param context The context - WebCrawlerApp object.
  1280. * @param index The index of the item that was clicked.
  1281. */
  1282. void web_crawler_setting_item_headers_clicked(void *context, uint32_t index)
  1283. {
  1284. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1285. furi_check(app, "WebCrawlerApp is NULL");
  1286. UNUSED(index);
  1287. if (!app->uart_text_input)
  1288. {
  1289. FURI_LOG_E(TAG, "Text input is NULL");
  1290. return;
  1291. }
  1292. if (!app->headers)
  1293. {
  1294. FURI_LOG_E(TAG, "Headers is NULL");
  1295. return;
  1296. }
  1297. if (!app->temp_buffer_headers)
  1298. {
  1299. FURI_LOG_E(TAG, "Temp buffer headers is NULL");
  1300. return;
  1301. }
  1302. // Initialize temp_buffer with existing headers
  1303. if (app->headers && strlen(app->headers) > 0)
  1304. {
  1305. strncpy(app->temp_buffer_headers, app->headers, app->temp_buffer_size_headers - 1);
  1306. }
  1307. else
  1308. {
  1309. strncpy(app->temp_buffer_headers, "{\"Content-Type\":\"application/json\",\"key\":\"value\"}", app->temp_buffer_size_headers - 1);
  1310. }
  1311. app->temp_buffer_headers[app->temp_buffer_size_headers - 1] = '\0';
  1312. // Configure the text input
  1313. bool clear_previous_text = false;
  1314. uart_text_input_set_result_callback(
  1315. app->uart_text_input,
  1316. web_crawler_set_headers_updated,
  1317. app,
  1318. app->temp_buffer_headers,
  1319. app->temp_buffer_size_headers,
  1320. clear_previous_text);
  1321. // Set the previous callback to return to Configure screen
  1322. view_set_previous_callback(
  1323. uart_text_input_get_view(app->uart_text_input),
  1324. web_crawler_back_to_request_callback);
  1325. // Show text input dialog
  1326. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewInput);
  1327. }
  1328. /**
  1329. * @brief Handler for payload configuration item click.
  1330. * @param context The context - WebCrawlerApp object.
  1331. * @param index The index of the item that was clicked.
  1332. */
  1333. void web_crawler_setting_item_payload_clicked(void *context, uint32_t index)
  1334. {
  1335. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1336. furi_check(app, "WebCrawlerApp is NULL");
  1337. UNUSED(index);
  1338. if (!app->uart_text_input)
  1339. {
  1340. FURI_LOG_E(TAG, "Text input is NULL");
  1341. return;
  1342. }
  1343. // Initialize temp_buffer with existing payload
  1344. if (app->payload && strlen(app->payload) > 0)
  1345. {
  1346. strncpy(app->temp_buffer_payload, app->payload, app->temp_buffer_size_payload - 1);
  1347. }
  1348. else
  1349. {
  1350. strncpy(app->temp_buffer_payload, "{\"key\":\"value\"}", app->temp_buffer_size_payload - 1);
  1351. }
  1352. app->temp_buffer_payload[app->temp_buffer_size_payload - 1] = '\0';
  1353. // Configure the text input
  1354. bool clear_previous_text = false;
  1355. uart_text_input_set_result_callback(
  1356. app->uart_text_input,
  1357. web_crawler_set_payload_updated,
  1358. app,
  1359. app->temp_buffer_payload,
  1360. app->temp_buffer_size_payload,
  1361. clear_previous_text);
  1362. // Set the previous callback to return to Configure screen
  1363. view_set_previous_callback(
  1364. uart_text_input_get_view(app->uart_text_input),
  1365. web_crawler_back_to_request_callback);
  1366. // Show text input dialog
  1367. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewInput);
  1368. }
  1369. /**
  1370. * @brief Handler for SSID configuration item click.
  1371. * @param context The context - WebCrawlerApp object.
  1372. * @param index The index of the item that was clicked.
  1373. */
  1374. void web_crawler_setting_item_ssid_clicked(void *context, uint32_t index)
  1375. {
  1376. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1377. furi_check(app, "WebCrawlerApp is NULL");
  1378. UNUSED(index);
  1379. if (!app->uart_text_input)
  1380. {
  1381. FURI_LOG_E(TAG, "Text input is NULL");
  1382. return;
  1383. }
  1384. // Initialize temp_buffer with existing SSID
  1385. if (app->ssid && strlen(app->ssid) > 0)
  1386. {
  1387. strncpy(app->temp_buffer_ssid, app->ssid, app->temp_buffer_size_ssid - 1);
  1388. }
  1389. else
  1390. {
  1391. strncpy(app->temp_buffer_ssid, "", app->temp_buffer_size_ssid - 1);
  1392. }
  1393. app->temp_buffer_ssid[app->temp_buffer_size_ssid - 1] = '\0';
  1394. // Configure the text input
  1395. bool clear_previous_text = false;
  1396. uart_text_input_set_result_callback(
  1397. app->uart_text_input,
  1398. web_crawler_set_ssid_updated,
  1399. app,
  1400. app->temp_buffer_ssid,
  1401. app->temp_buffer_size_ssid,
  1402. clear_previous_text);
  1403. // Set the previous callback to return to Configure screen
  1404. view_set_previous_callback(
  1405. uart_text_input_get_view(app->uart_text_input),
  1406. web_crawler_back_to_wifi_callback);
  1407. // Show text input dialog
  1408. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewInput);
  1409. }
  1410. /**
  1411. * @brief Handler for Password configuration item click.
  1412. * @param context The context - WebCrawlerApp object.
  1413. * @param index The index of the item that was clicked.
  1414. */
  1415. void web_crawler_setting_item_password_clicked(void *context, uint32_t index)
  1416. {
  1417. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1418. furi_check(app, "WebCrawlerApp is NULL");
  1419. UNUSED(index);
  1420. if (!app->uart_text_input)
  1421. {
  1422. FURI_LOG_E(TAG, "Text input is NULL");
  1423. return;
  1424. }
  1425. // Initialize temp_buffer with existing password
  1426. strncpy(app->temp_buffer_password, app->password, app->temp_buffer_size_password - 1);
  1427. app->temp_buffer_password[app->temp_buffer_size_password - 1] = '\0';
  1428. // Configure the text input
  1429. bool clear_previous_text = false;
  1430. uart_text_input_set_result_callback(
  1431. app->uart_text_input,
  1432. web_crawler_set_password_update,
  1433. app,
  1434. app->temp_buffer_password,
  1435. app->temp_buffer_size_password,
  1436. clear_previous_text);
  1437. // Set the previous callback to return to Configure screen
  1438. view_set_previous_callback(
  1439. uart_text_input_get_view(app->uart_text_input),
  1440. web_crawler_back_to_wifi_callback);
  1441. // Show text input dialog
  1442. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewInput);
  1443. }
  1444. /**
  1445. * @brief Handler for File Type configuration item click.
  1446. * @param context The context - WebCrawlerApp object.
  1447. * @param index The index of the item that was clicked.
  1448. */
  1449. void web_crawler_setting_item_file_type_clicked(void *context, uint32_t index)
  1450. {
  1451. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1452. furi_check(app, "WebCrawlerApp is NULL");
  1453. UNUSED(index);
  1454. if (!app->uart_text_input)
  1455. {
  1456. FURI_LOG_E(TAG, "Text input is NULL");
  1457. return;
  1458. }
  1459. // Initialize temp_buffer with existing file_type
  1460. if (app->file_type && strlen(app->file_type) > 0)
  1461. {
  1462. strncpy(app->temp_buffer_file_type, app->file_type, app->temp_buffer_size_file_type - 1);
  1463. }
  1464. else
  1465. {
  1466. strncpy(app->temp_buffer_file_type, ".txt", app->temp_buffer_size_file_type - 1);
  1467. }
  1468. app->temp_buffer_file_type[app->temp_buffer_size_file_type - 1] = '\0';
  1469. // Configure the text input
  1470. bool clear_previous_text = false;
  1471. uart_text_input_set_result_callback(
  1472. app->uart_text_input,
  1473. web_crawler_set_file_type_update,
  1474. app,
  1475. app->temp_buffer_file_type,
  1476. app->temp_buffer_size_file_type,
  1477. clear_previous_text);
  1478. // Set the previous callback to return to Configure screen
  1479. view_set_previous_callback(
  1480. uart_text_input_get_view(app->uart_text_input),
  1481. web_crawler_back_to_file_callback);
  1482. // Show text input dialog
  1483. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewInput);
  1484. }
  1485. /**
  1486. * @brief Handler for File Rename configuration item click.
  1487. * @param context The context - WebCrawlerApp object.
  1488. * @param index The index of the item that was clicked.
  1489. */
  1490. void web_crawler_setting_item_file_rename_clicked(void *context, uint32_t index)
  1491. {
  1492. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1493. furi_check(app, "WebCrawlerApp is NULL");
  1494. UNUSED(index);
  1495. if (!app->uart_text_input)
  1496. {
  1497. FURI_LOG_E(TAG, "Text input is NULL");
  1498. return;
  1499. }
  1500. // Initialize temp_buffer with existing file_rename
  1501. if (app->file_rename && strlen(app->file_rename) > 0)
  1502. {
  1503. strncpy(app->temp_buffer_file_rename, app->file_rename, app->temp_buffer_size_file_rename - 1);
  1504. }
  1505. else
  1506. {
  1507. strncpy(app->temp_buffer_file_rename, "received_data", app->temp_buffer_size_file_rename - 1);
  1508. }
  1509. app->temp_buffer_file_rename[app->temp_buffer_size_file_rename - 1] = '\0';
  1510. // Configure the text input
  1511. bool clear_previous_text = false;
  1512. uart_text_input_set_result_callback(
  1513. app->uart_text_input,
  1514. web_crawler_set_file_rename_update,
  1515. app,
  1516. app->temp_buffer_file_rename,
  1517. app->temp_buffer_size_file_rename,
  1518. clear_previous_text);
  1519. // Set the previous callback to return to Configure screen
  1520. view_set_previous_callback(
  1521. uart_text_input_get_view(app->uart_text_input),
  1522. web_crawler_back_to_file_callback);
  1523. // Show text input dialog
  1524. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewInput);
  1525. }
  1526. /**
  1527. * @brief Handler for File Delete configuration item click.
  1528. * @param context The context - WebCrawlerApp object.
  1529. * @param index The index of the item that was clicked.
  1530. */
  1531. void web_crawler_setting_item_file_delete_clicked(void *context, uint32_t index)
  1532. {
  1533. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1534. furi_check(app, "WebCrawlerApp is NULL");
  1535. UNUSED(index);
  1536. free_widget(app);
  1537. if (!alloc_widget(app, WebCrawlerViewFileDelete))
  1538. {
  1539. FURI_LOG_E(TAG, "web_crawler_setting_item_file_delete_clicked: Failed to allocate widget");
  1540. return;
  1541. }
  1542. if (!delete_received_data())
  1543. {
  1544. FURI_LOG_E(TAG, "Failed to delete file");
  1545. }
  1546. // Set the previous callback to return to Configure screen
  1547. view_set_previous_callback(
  1548. widget_get_view(app->widget),
  1549. web_crawler_back_to_file_callback);
  1550. // Show text input dialog
  1551. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewWidget);
  1552. }
  1553. void web_crawler_setting_item_file_read_clicked(void *context, uint32_t index)
  1554. {
  1555. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1556. furi_check(app, "WebCrawlerApp is NULL");
  1557. UNUSED(index);
  1558. free_widget(app);
  1559. if (!alloc_widget(app, WebCrawlerViewFileRead))
  1560. {
  1561. FURI_LOG_E(TAG, "web_crawler_setting_item_file_read_clicked: Failed to allocate widget");
  1562. return;
  1563. }
  1564. widget_reset(app->widget);
  1565. char file_path[256];
  1566. char file_rename[128];
  1567. char file_type[16];
  1568. if (load_char("file_rename", file_rename, 128) && load_char("file_type", file_type, 16))
  1569. {
  1570. snprintf(file_path, sizeof(file_path), "%s%s%s", RECEIVED_DATA_PATH, file_rename, file_type);
  1571. }
  1572. else
  1573. {
  1574. snprintf(file_path, sizeof(file_path), "%s%s%s", RECEIVED_DATA_PATH, "received_data", ".txt");
  1575. }
  1576. // load the received data from the saved file
  1577. FuriString *received_data = flipper_http_load_from_file(file_path);
  1578. if (received_data == NULL)
  1579. {
  1580. FURI_LOG_E(TAG, "Failed to load received data from file.");
  1581. if (app->widget)
  1582. {
  1583. widget_add_text_scroll_element(
  1584. app->widget,
  1585. 0,
  1586. 0,
  1587. 128,
  1588. 64, "File is empty.");
  1589. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewWidget);
  1590. }
  1591. return;
  1592. }
  1593. widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, furi_string_get_cstr(received_data));
  1594. furi_string_free(received_data);
  1595. // Set the previous callback to return to Configure screen
  1596. view_set_previous_callback(
  1597. widget_get_view(app->widget),
  1598. web_crawler_back_to_file_callback);
  1599. // Show text input dialog
  1600. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewWidget);
  1601. }
  1602. static void web_crawler_widget_set_text(char *message, Widget **widget)
  1603. {
  1604. if (widget == NULL)
  1605. {
  1606. FURI_LOG_E(TAG, "flip_weather_set_widget_text - widget is NULL");
  1607. DEV_CRASH();
  1608. return;
  1609. }
  1610. if (message == NULL)
  1611. {
  1612. FURI_LOG_E(TAG, "flip_weather_set_widget_text - message is NULL");
  1613. DEV_CRASH();
  1614. return;
  1615. }
  1616. widget_reset(*widget);
  1617. uint32_t message_length = strlen(message); // Length of the message
  1618. uint32_t i = 0; // Index tracker
  1619. uint32_t formatted_index = 0; // Tracker for where we are in the formatted message
  1620. char *formatted_message; // Buffer to hold the final formatted message
  1621. // Allocate buffer with double the message length plus one for safety
  1622. if (!easy_flipper_set_buffer(&formatted_message, message_length * 2 + 1))
  1623. {
  1624. return;
  1625. }
  1626. while (i < message_length)
  1627. {
  1628. uint32_t max_line_length = 31; // Maximum characters per line
  1629. uint32_t remaining_length = message_length - i; // Remaining characters
  1630. uint32_t line_length = (remaining_length < max_line_length) ? remaining_length : max_line_length;
  1631. // Check for newline character within the current segment
  1632. uint32_t newline_pos = i;
  1633. bool found_newline = false;
  1634. for (; newline_pos < i + line_length && newline_pos < message_length; newline_pos++)
  1635. {
  1636. if (message[newline_pos] == '\n')
  1637. {
  1638. found_newline = true;
  1639. break;
  1640. }
  1641. }
  1642. if (found_newline)
  1643. {
  1644. // If newline found, set line_length up to the newline
  1645. line_length = newline_pos - i;
  1646. }
  1647. // Temporary buffer to hold the current line
  1648. char line[32];
  1649. strncpy(line, message + i, line_length);
  1650. line[line_length] = '\0';
  1651. // If newline was found, skip it for the next iteration
  1652. if (found_newline)
  1653. {
  1654. i += line_length + 1; // +1 to skip the '\n' character
  1655. }
  1656. else
  1657. {
  1658. // Check if the line ends in the middle of a word and adjust accordingly
  1659. if (line_length == max_line_length && message[i + line_length] != '\0' && message[i + line_length] != ' ')
  1660. {
  1661. // Find the last space within the current line to avoid breaking a word
  1662. char *last_space = strrchr(line, ' ');
  1663. if (last_space != NULL)
  1664. {
  1665. // Adjust the line_length to avoid cutting the word
  1666. line_length = last_space - line;
  1667. line[line_length] = '\0'; // Null-terminate at the space
  1668. }
  1669. }
  1670. // Move the index forward by the determined line_length
  1671. i += line_length;
  1672. // Skip any spaces at the beginning of the next line
  1673. while (i < message_length && message[i] == ' ')
  1674. {
  1675. i++;
  1676. }
  1677. }
  1678. // Manually copy the fixed line into the formatted_message buffer
  1679. for (uint32_t j = 0; j < line_length; j++)
  1680. {
  1681. formatted_message[formatted_index++] = line[j];
  1682. }
  1683. // Add a newline character for line spacing
  1684. formatted_message[formatted_index++] = '\n';
  1685. }
  1686. // Null-terminate the formatted_message
  1687. formatted_message[formatted_index] = '\0';
  1688. // Add the formatted message to the widget
  1689. widget_add_text_scroll_element(*widget, 0, 0, 128, 64, formatted_message);
  1690. }
  1691. void web_crawler_loader_draw_callback(Canvas *canvas, void *model)
  1692. {
  1693. if (!canvas || !model)
  1694. {
  1695. FURI_LOG_E(TAG, "web_crawler_loader_draw_callback - canvas or model is NULL");
  1696. return;
  1697. }
  1698. DataLoaderModel *data_loader_model = (DataLoaderModel *)model;
  1699. SerialState http_state = data_loader_model->fhttp->state;
  1700. DataState data_state = data_loader_model->data_state;
  1701. char *title = data_loader_model->title;
  1702. canvas_set_font(canvas, FontSecondary);
  1703. if (http_state == INACTIVE)
  1704. {
  1705. canvas_draw_str(canvas, 0, 7, "Wifi Dev Board disconnected.");
  1706. canvas_draw_str(canvas, 0, 17, "Please connect to the board.");
  1707. canvas_draw_str(canvas, 0, 32, "If your board is connected,");
  1708. canvas_draw_str(canvas, 0, 42, "make sure you have flashed");
  1709. canvas_draw_str(canvas, 0, 52, "your WiFi Devboard with the");
  1710. canvas_draw_str(canvas, 0, 62, "latest FlipperHTTP flash.");
  1711. return;
  1712. }
  1713. if (data_state == DataStateError || data_state == DataStateParseError)
  1714. {
  1715. web_crawler_draw_error(canvas, data_loader_model);
  1716. return;
  1717. }
  1718. canvas_draw_str(canvas, 0, 7, title);
  1719. canvas_draw_str(canvas, 0, 17, "Loading...");
  1720. if (data_state == DataStateInitial)
  1721. {
  1722. return;
  1723. }
  1724. if (http_state == SENDING)
  1725. {
  1726. canvas_draw_str(canvas, 0, 27, "Fetching...");
  1727. return;
  1728. }
  1729. if (http_state == RECEIVING || data_state == DataStateRequested)
  1730. {
  1731. canvas_draw_str(canvas, 0, 27, "Receiving...");
  1732. return;
  1733. }
  1734. if (http_state == IDLE && data_state == DataStateReceived)
  1735. {
  1736. canvas_draw_str(canvas, 0, 27, "Processing...");
  1737. return;
  1738. }
  1739. if (http_state == IDLE && data_state == DataStateParsed)
  1740. {
  1741. canvas_draw_str(canvas, 0, 27, "Processed...");
  1742. return;
  1743. }
  1744. }
  1745. static void web_crawler_loader_process_callback(void *context)
  1746. {
  1747. if (context == NULL)
  1748. {
  1749. FURI_LOG_E(TAG, "web_crawler_loader_process_callback - context is NULL");
  1750. DEV_CRASH();
  1751. return;
  1752. }
  1753. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1754. View *view = app->view_loader;
  1755. DataState current_data_state;
  1756. DataLoaderModel *loader_model = NULL;
  1757. with_view_model(view, DataLoaderModel * model, { current_data_state = model->data_state; loader_model = model; }, false);
  1758. if (!loader_model || !loader_model->fhttp)
  1759. {
  1760. FURI_LOG_E(TAG, "Model or fhttp is NULL");
  1761. DEV_CRASH();
  1762. return;
  1763. }
  1764. if (current_data_state == DataStateInitial)
  1765. {
  1766. with_view_model(
  1767. view,
  1768. DataLoaderModel * model,
  1769. {
  1770. model->data_state = DataStateRequested;
  1771. DataLoaderFetch fetch = model->fetcher;
  1772. if (fetch == NULL)
  1773. {
  1774. FURI_LOG_E(TAG, "Model doesn't have Fetch function assigned.");
  1775. model->data_state = DataStateError;
  1776. return;
  1777. }
  1778. // Clear any previous responses
  1779. strncpy(model->fhttp->last_response, "", 1);
  1780. bool request_status = fetch(model);
  1781. if (!request_status)
  1782. {
  1783. model->data_state = DataStateError;
  1784. }
  1785. },
  1786. true);
  1787. }
  1788. else if (current_data_state == DataStateRequested || current_data_state == DataStateError)
  1789. {
  1790. if (loader_model->fhttp->state == IDLE && loader_model->fhttp->last_response != NULL)
  1791. {
  1792. if (strstr(loader_model->fhttp->last_response, "[PONG]") != NULL)
  1793. {
  1794. FURI_LOG_DEV(TAG, "PONG received.");
  1795. }
  1796. else if (strncmp(loader_model->fhttp->last_response, "[SUCCESS]", 9) == 0)
  1797. {
  1798. FURI_LOG_DEV(TAG, "SUCCESS received. %s", loader_model->fhttp->last_response ? loader_model->fhttp->last_response : "NULL");
  1799. }
  1800. else if (strncmp(loader_model->fhttp->last_response, "[ERROR]", 9) == 0)
  1801. {
  1802. FURI_LOG_DEV(TAG, "ERROR received. %s", loader_model->fhttp->last_response ? loader_model->fhttp->last_response : "NULL");
  1803. }
  1804. else if (strlen(loader_model->fhttp->last_response) == 0)
  1805. {
  1806. // Still waiting on response
  1807. }
  1808. else
  1809. {
  1810. with_view_model(view, DataLoaderModel * model, { model->data_state = DataStateReceived; }, true);
  1811. }
  1812. }
  1813. else if (loader_model->fhttp->state == SENDING || loader_model->fhttp->state == RECEIVING)
  1814. {
  1815. // continue waiting
  1816. }
  1817. else if (loader_model->fhttp->state == INACTIVE)
  1818. {
  1819. // inactive. try again
  1820. }
  1821. else if (loader_model->fhttp->state == ISSUE)
  1822. {
  1823. with_view_model(view, DataLoaderModel * model, { model->data_state = DataStateError; }, true);
  1824. }
  1825. else
  1826. {
  1827. FURI_LOG_DEV(TAG, "Unexpected state: %d lastresp: %s", loader_model->fhttp->state, loader_model->fhttp->last_response ? loader_model->fhttp->last_response : "NULL");
  1828. DEV_CRASH();
  1829. }
  1830. }
  1831. else if (current_data_state == DataStateReceived)
  1832. {
  1833. with_view_model(
  1834. view,
  1835. DataLoaderModel * model,
  1836. {
  1837. char *data_text;
  1838. if (model->parser == NULL)
  1839. {
  1840. data_text = NULL;
  1841. FURI_LOG_DEV(TAG, "Parser is NULL");
  1842. DEV_CRASH();
  1843. }
  1844. else
  1845. {
  1846. data_text = model->parser(model);
  1847. }
  1848. FURI_LOG_DEV(TAG, "Parsed data: %s\r\ntext: %s", model->fhttp->last_response ? model->fhttp->last_response : "NULL", data_text ? data_text : "NULL");
  1849. model->data_text = data_text;
  1850. if (data_text == NULL)
  1851. {
  1852. model->data_state = DataStateParseError;
  1853. }
  1854. else
  1855. {
  1856. model->data_state = DataStateParsed;
  1857. }
  1858. },
  1859. true);
  1860. }
  1861. else if (current_data_state == DataStateParsed)
  1862. {
  1863. with_view_model(
  1864. view,
  1865. DataLoaderModel * model,
  1866. {
  1867. if (++model->request_index < model->request_count)
  1868. {
  1869. model->data_state = DataStateInitial;
  1870. }
  1871. else
  1872. {
  1873. web_crawler_widget_set_text(model->data_text != NULL ? model->data_text : "", &app->widget_result);
  1874. if (model->data_text != NULL)
  1875. {
  1876. free(model->data_text);
  1877. model->data_text = NULL;
  1878. }
  1879. view_set_previous_callback(widget_get_view(app->widget_result), model->back_callback);
  1880. view_dispatcher_switch_to_view(app->view_dispatcher, WebCrawlerViewWidgetResult);
  1881. }
  1882. },
  1883. true);
  1884. }
  1885. }
  1886. static void web_crawler_loader_timer_callback(void *context)
  1887. {
  1888. if (context == NULL)
  1889. {
  1890. FURI_LOG_E(TAG, "web_crawler_loader_timer_callback - context is NULL");
  1891. DEV_CRASH();
  1892. return;
  1893. }
  1894. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1895. view_dispatcher_send_custom_event(app->view_dispatcher, WebCrawlerCustomEventProcess);
  1896. }
  1897. static void web_crawler_loader_on_enter(void *context)
  1898. {
  1899. if (context == NULL)
  1900. {
  1901. FURI_LOG_E(TAG, "web_crawler_loader_on_enter - context is NULL");
  1902. DEV_CRASH();
  1903. return;
  1904. }
  1905. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1906. View *view = app->view_loader;
  1907. with_view_model(
  1908. view,
  1909. DataLoaderModel * model,
  1910. {
  1911. view_set_previous_callback(view, model->back_callback);
  1912. if (model->timer == NULL)
  1913. {
  1914. model->timer = furi_timer_alloc(web_crawler_loader_timer_callback, FuriTimerTypePeriodic, app);
  1915. }
  1916. furi_timer_start(model->timer, 250);
  1917. },
  1918. true);
  1919. }
  1920. static void web_crawler_loader_on_exit(void *context)
  1921. {
  1922. if (context == NULL)
  1923. {
  1924. FURI_LOG_E(TAG, "web_crawler_loader_on_exit - context is NULL");
  1925. DEV_CRASH();
  1926. return;
  1927. }
  1928. WebCrawlerApp *app = (WebCrawlerApp *)context;
  1929. View *view = app->view_loader;
  1930. with_view_model(
  1931. view,
  1932. DataLoaderModel * model,
  1933. {
  1934. if (model->timer)
  1935. {
  1936. furi_timer_stop(model->timer);
  1937. }
  1938. },
  1939. false);
  1940. }
  1941. void web_crawler_loader_init(View *view)
  1942. {
  1943. if (view == NULL)
  1944. {
  1945. FURI_LOG_E(TAG, "web_crawler_loader_init - view is NULL");
  1946. DEV_CRASH();
  1947. return;
  1948. }
  1949. view_allocate_model(view, ViewModelTypeLocking, sizeof(DataLoaderModel));
  1950. view_set_enter_callback(view, web_crawler_loader_on_enter);
  1951. view_set_exit_callback(view, web_crawler_loader_on_exit);
  1952. }
  1953. void web_crawler_loader_free_model(View *view)
  1954. {
  1955. if (view == NULL)
  1956. {
  1957. FURI_LOG_E(TAG, "web_crawler_loader_free_model - view is NULL");
  1958. DEV_CRASH();
  1959. return;
  1960. }
  1961. with_view_model(
  1962. view,
  1963. DataLoaderModel * model,
  1964. {
  1965. if (model->timer)
  1966. {
  1967. furi_timer_free(model->timer);
  1968. model->timer = NULL;
  1969. }
  1970. if (model->parser_context)
  1971. {
  1972. // do not free the context here, it is the app context
  1973. // free(model->parser_context);
  1974. // model->parser_context = NULL;
  1975. }
  1976. if (model->fhttp)
  1977. {
  1978. flipper_http_free(model->fhttp);
  1979. model->fhttp = NULL;
  1980. }
  1981. },
  1982. false);
  1983. }
  1984. bool web_crawler_custom_event_callback(void *context, uint32_t index)
  1985. {
  1986. if (context == NULL)
  1987. {
  1988. FURI_LOG_E(TAG, "web_crawler_custom_event_callback - context is NULL");
  1989. DEV_CRASH();
  1990. return false;
  1991. }
  1992. switch (index)
  1993. {
  1994. case WebCrawlerCustomEventProcess:
  1995. web_crawler_loader_process_callback(context);
  1996. return true;
  1997. default:
  1998. FURI_LOG_DEV(TAG, "web_crawler_custom_event_callback. Unknown index: %ld", index);
  1999. return false;
  2000. }
  2001. }
  2002. 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)
  2003. {
  2004. if (app == NULL)
  2005. {
  2006. FURI_LOG_E(TAG, "web_crawler_generic_switch_to_view - app is NULL");
  2007. DEV_CRASH();
  2008. return;
  2009. }
  2010. View *view = app->view_loader;
  2011. if (view == NULL)
  2012. {
  2013. FURI_LOG_E(TAG, "web_crawler_generic_switch_to_view - view is NULL");
  2014. DEV_CRASH();
  2015. return;
  2016. }
  2017. with_view_model(
  2018. view,
  2019. DataLoaderModel * model,
  2020. {
  2021. model->title = title;
  2022. model->fetcher = fetcher;
  2023. model->parser = parser;
  2024. model->request_index = 0;
  2025. model->request_count = request_count;
  2026. model->back_callback = back;
  2027. model->data_state = DataStateInitial;
  2028. model->data_text = NULL;
  2029. //
  2030. model->parser_context = app;
  2031. if (!model->fhttp)
  2032. {
  2033. model->fhttp = flipper_http_alloc();
  2034. }
  2035. },
  2036. true);
  2037. view_dispatcher_switch_to_view(app->view_dispatcher, view_id);
  2038. }