callback.c 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001
  1. #include <callback/callback.h>
  2. #include <callback/loader.h>
  3. #include <callback/free.h>
  4. #include <callback/alloc.h>
  5. uint32_t callback_exit_app(void *context)
  6. {
  7. UNUSED(context);
  8. return VIEW_NONE;
  9. }
  10. uint32_t callback_submenu_ap(void *context)
  11. {
  12. FlipWiFiApp *app = (FlipWiFiApp *)context;
  13. furi_check(app);
  14. back_from_ap = true;
  15. return FlipWiFiViewSubmenu;
  16. }
  17. uint32_t callback_to_submenu_main(void *context)
  18. {
  19. UNUSED(context);
  20. ssid_index = 0;
  21. return FlipWiFiViewSubmenuMain;
  22. }
  23. uint32_t callback_to_submenu_scan(void *context)
  24. {
  25. UNUSED(context);
  26. ssid_index = 0;
  27. return FlipWiFiViewSubmenu;
  28. }
  29. uint32_t callback_to_submenu_saved(void *context)
  30. {
  31. UNUSED(context);
  32. ssid_index = 0;
  33. return FlipWiFiViewSubmenu;
  34. }
  35. void callback_custom_command_updated(void *context)
  36. {
  37. FlipWiFiApp *app = (FlipWiFiApp *)context;
  38. furi_check(app);
  39. if (!app->fhttp)
  40. {
  41. FURI_LOG_E(TAG, "FlipperHTTP is NULL");
  42. return;
  43. }
  44. if (!app->uart_text_input_temp_buffer)
  45. {
  46. FURI_LOG_E(TAG, "Text input buffer is NULL");
  47. return;
  48. }
  49. if (!app->uart_text_input_temp_buffer[0])
  50. {
  51. FURI_LOG_E(TAG, "Text input buffer is empty");
  52. return;
  53. }
  54. // Send the custom command
  55. flipper_http_send_data(app->fhttp, app->uart_text_input_temp_buffer);
  56. uint32_t timeout = 50; // 5 seconds / 100ms iterations
  57. while ((app->fhttp->last_response == NULL || strlen(app->fhttp->last_response) == 0) && timeout > 0)
  58. {
  59. furi_delay_ms(100);
  60. timeout--;
  61. }
  62. // Switch to the view
  63. char response[100];
  64. snprintf(response, sizeof(response), "%s", app->fhttp->last_response);
  65. easy_flipper_dialog("", response);
  66. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  67. }
  68. void callback_ap_ssid_updated(void *context)
  69. {
  70. FlipWiFiApp *app = (FlipWiFiApp *)context;
  71. furi_check(app);
  72. if (!app->uart_text_input_temp_buffer)
  73. {
  74. FURI_LOG_E(TAG, "Text input buffer is NULL");
  75. return;
  76. }
  77. if (!app->uart_text_input_temp_buffer[0])
  78. {
  79. FURI_LOG_E(TAG, "Text input buffer is empty");
  80. return;
  81. }
  82. save_char("ap_ssid", app->uart_text_input_temp_buffer);
  83. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  84. }
  85. void callback_text_updated_deauth(void *context)
  86. {
  87. FlipWiFiApp *app = (FlipWiFiApp *)context;
  88. furi_check(app);
  89. if (!app->uart_text_input_temp_buffer)
  90. {
  91. FURI_LOG_E(TAG, "Text input buffer is NULL");
  92. return;
  93. }
  94. if (!app->uart_text_input_temp_buffer[0])
  95. {
  96. FURI_LOG_E(TAG, "Text input buffer is empty");
  97. return;
  98. }
  99. save_char("deauth_ssid", app->uart_text_input_temp_buffer);
  100. if (!app->fhttp)
  101. {
  102. app->fhttp = flipper_http_alloc();
  103. if (!app->fhttp)
  104. {
  105. easy_flipper_dialog("[ERROR]", "Failed to initialize flipper http");
  106. return;
  107. }
  108. }
  109. Loading *loading = loading_alloc();
  110. int32_t loading_view_id = 87654321; // Random ID
  111. view_dispatcher_add_view(app->view_dispatcher, loading_view_id, loading_get_view(loading));
  112. view_dispatcher_switch_to_view(app->view_dispatcher, loading_view_id);
  113. if (!flipper_http_deauth_start(app->fhttp, app->uart_text_input_temp_buffer))
  114. {
  115. easy_flipper_dialog("[ERROR]", "Failed to start deauth attack");
  116. flipper_http_free(app->fhttp);
  117. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewTextInput);
  118. view_dispatcher_remove_view(app->view_dispatcher, loading_view_id);
  119. loading_free(loading);
  120. loading = NULL;
  121. return;
  122. }
  123. while (app->fhttp->last_response == NULL || strlen(app->fhttp->last_response) == 0)
  124. {
  125. furi_delay_ms(100);
  126. }
  127. if (strstr(app->fhttp->last_response, "[DEAUTH/STARTING]") == NULL)
  128. {
  129. char response[256];
  130. snprintf(response, sizeof(response), "Failed to start deauth attack:\n%s", app->fhttp->last_response);
  131. easy_flipper_dialog("[ERROR]", response);
  132. flipper_http_free(app->fhttp);
  133. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewTextInput);
  134. view_dispatcher_remove_view(app->view_dispatcher, loading_view_id);
  135. loading_free(loading);
  136. loading = NULL;
  137. return;
  138. }
  139. while (strstr(app->fhttp->last_response, "[DEAUTH/STARTING]") != NULL)
  140. {
  141. furi_delay_ms(100);
  142. }
  143. if (strstr(app->fhttp->last_response, "[ERROR]") != NULL)
  144. {
  145. char response[256];
  146. snprintf(response, sizeof(response), "Failed to start deauth attack:\n%s", app->fhttp->last_response);
  147. easy_flipper_dialog("[ERROR]", response);
  148. flipper_http_free(app->fhttp);
  149. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewTextInput);
  150. view_dispatcher_remove_view(app->view_dispatcher, loading_view_id);
  151. loading_free(loading);
  152. loading = NULL;
  153. return;
  154. }
  155. if (!alloc_views(app, FlipWiFiViewWiFiDeauth))
  156. {
  157. FURI_LOG_E(TAG, "Failed to allocate view for WiFi Deauth");
  158. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewTextInput);
  159. view_dispatcher_remove_view(app->view_dispatcher, loading_view_id);
  160. loading_free(loading);
  161. loading = NULL;
  162. return;
  163. }
  164. // switch to a canvas view
  165. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewGeneric);
  166. view_dispatcher_remove_view(app->view_dispatcher, loading_view_id);
  167. loading_free(loading);
  168. loading = NULL;
  169. }
  170. void callback_redraw_submenu_saved(void *context)
  171. {
  172. // re draw the saved submenu
  173. FlipWiFiApp *app = (FlipWiFiApp *)context;
  174. furi_check(app);
  175. if (!app->submenu_wifi)
  176. {
  177. FURI_LOG_E(TAG, "Submenu is NULL");
  178. return;
  179. }
  180. if (!wifi_playlist)
  181. {
  182. FURI_LOG_E(TAG, "WiFi Playlist is NULL");
  183. return;
  184. }
  185. submenu_reset(app->submenu_wifi);
  186. submenu_set_header(app->submenu_wifi, "Saved APs");
  187. submenu_add_item(app->submenu_wifi, "[Add Network]", FlipWiFiSubmenuIndexWiFiSavedAddSSID, callback_submenu_choices, app);
  188. for (size_t i = 0; i < wifi_playlist->count; i++)
  189. {
  190. submenu_add_item(app->submenu_wifi, wifi_playlist->ssids[i], FlipWiFiSubmenuIndexWiFiSavedStart + i, callback_submenu_choices, app);
  191. }
  192. }
  193. // Callback for drawing the main screen
  194. void callback_view_draw_callback_scan(Canvas *canvas, void *model)
  195. {
  196. UNUSED(model);
  197. canvas_clear(canvas);
  198. canvas_set_font(canvas, FontPrimary);
  199. canvas_draw_str(canvas, 0, 10, ssid_list[ssid_index]);
  200. canvas_draw_icon(canvas, 0, 53, &I_ButtonBACK_10x8);
  201. canvas_draw_str_aligned(canvas, 12, 54, AlignLeft, AlignTop, "Back");
  202. canvas_draw_icon(canvas, 96, 53, &I_ButtonRight_4x7);
  203. canvas_draw_str_aligned(canvas, 103, 54, AlignLeft, AlignTop, "Add");
  204. }
  205. void callback_view_draw_callback_saved(Canvas *canvas, void *model)
  206. {
  207. UNUSED(model);
  208. canvas_clear(canvas);
  209. canvas_set_font(canvas, FontPrimary);
  210. canvas_draw_str(canvas, 0, 10, current_ssid);
  211. canvas_set_font(canvas, FontSecondary);
  212. char password[72];
  213. snprintf(password, sizeof(password), "Pass: %s", current_password);
  214. canvas_draw_str(canvas, 0, 20, password);
  215. canvas_draw_icon(canvas, 0, 54, &I_ButtonLeft_4x7);
  216. canvas_draw_str_aligned(canvas, 7, 54, AlignLeft, AlignTop, "Delete");
  217. canvas_draw_icon(canvas, 37, 53, &I_ButtonBACK_10x8);
  218. canvas_draw_str_aligned(canvas, 49, 54, AlignLeft, AlignTop, "Back");
  219. canvas_draw_icon(canvas, 73, 54, &I_ButtonOK_7x7);
  220. canvas_draw_str_aligned(canvas, 81, 54, AlignLeft, AlignTop, "Set");
  221. canvas_draw_icon(canvas, 100, 54, &I_ButtonRight_4x7);
  222. canvas_draw_str_aligned(canvas, 107, 54, AlignLeft, AlignTop, "Edit");
  223. }
  224. // Callback for drawing the deauth screen
  225. void callback_view_draw_callback_deauth(Canvas *canvas, void *model)
  226. {
  227. UNUSED(model);
  228. canvas_clear(canvas);
  229. canvas_set_font(canvas, FontPrimary);
  230. canvas_draw_str(canvas, 0, 10, "Deauthing...");
  231. canvas_draw_icon(canvas, 0, 53, &I_ButtonBACK_10x8);
  232. canvas_draw_str_aligned(canvas, 12, 54, AlignLeft, AlignTop, "Hit Back to stop");
  233. }
  234. // Input callback for the view (async input handling)
  235. bool callback_view_input_callback_scan(InputEvent *event, void *context)
  236. {
  237. FlipWiFiApp *app = (FlipWiFiApp *)context;
  238. if (event->type == InputTypePress && event->key == InputKeyRight)
  239. {
  240. // switch to text input to set password
  241. free_text_inputs(app);
  242. if (!alloc_text_inputs(app, FlipWiFiViewTextInputScan))
  243. {
  244. FURI_LOG_E(TAG, "Failed to allocate text input for WiFi Saved Add Password");
  245. return false;
  246. }
  247. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewTextInput);
  248. return true;
  249. }
  250. return false;
  251. }
  252. // Input callback for the view (async input handling)
  253. bool callback_view_input_callback_deauth(InputEvent *event, void *context)
  254. {
  255. FlipWiFiApp *app = (FlipWiFiApp *)context;
  256. if (event->type == InputTypePress && event->key == InputKeyBack)
  257. {
  258. if (app->fhttp)
  259. {
  260. flipper_http_deauth_stop(app->fhttp);
  261. furi_delay_ms(200);
  262. flipper_http_free(app->fhttp);
  263. app->fhttp = NULL;
  264. }
  265. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenuMain);
  266. return true;
  267. }
  268. return false;
  269. }
  270. // Input callback for the view (async input handling)
  271. bool callback_view_input_callback_saved(InputEvent *event, void *context)
  272. {
  273. FlipWiFiApp *app = (FlipWiFiApp *)context;
  274. furi_check(app);
  275. if (event->type == InputTypePress && event->key == InputKeyRight)
  276. {
  277. // set text input buffer as the selected password
  278. strncpy(app->uart_text_input_temp_buffer, wifi_playlist->passwords[ssid_index], app->uart_text_input_buffer_size);
  279. // switch to text input to set password
  280. free_text_inputs(app);
  281. if (!alloc_text_inputs(app, FlipWiFiViewTextInputSaved))
  282. {
  283. FURI_LOG_E(TAG, "Failed to allocate text input for WiFi Saved");
  284. return false;
  285. }
  286. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewTextInput);
  287. return true;
  288. }
  289. else if (event->type == InputTypePress && event->key == InputKeyOk)
  290. {
  291. // save the settings
  292. save_settings(wifi_playlist->ssids[ssid_index], wifi_playlist->passwords[ssid_index]);
  293. // initialize uart
  294. FlipperHTTP *fhttp = flipper_http_alloc();
  295. if (!fhttp)
  296. {
  297. easy_flipper_dialog("[ERROR]", "Failed to initialize flipper http");
  298. return false;
  299. }
  300. // clear response
  301. if (fhttp->last_response)
  302. snprintf(fhttp->last_response, RX_BUF_SIZE, "%s", "");
  303. if (!flipper_http_save_wifi(fhttp, wifi_playlist->ssids[ssid_index], wifi_playlist->passwords[ssid_index]))
  304. {
  305. easy_flipper_dialog("[ERROR]", "Failed to save WiFi settings");
  306. return false;
  307. }
  308. while (!fhttp->last_response || strlen(fhttp->last_response) == 0)
  309. {
  310. furi_delay_ms(100);
  311. }
  312. // check success (if [SUCCESS] is in the response)
  313. if (strstr(fhttp->last_response, "[SUCCESS]") == NULL)
  314. {
  315. char response[256];
  316. snprintf(response, sizeof(response), "Failed to save WiFi settings:\n%s", fhttp->last_response);
  317. easy_flipper_dialog("[ERROR]", response);
  318. flipper_http_free(fhttp);
  319. return false;
  320. }
  321. flipper_http_free(fhttp);
  322. easy_flipper_dialog("[SUCCESS]", "All FlipperHTTP apps will now\nuse the selected network.");
  323. return true;
  324. }
  325. else if (event->type == InputTypePress && event->key == InputKeyLeft)
  326. {
  327. // shift the remaining ssids and passwords
  328. for (uint32_t i = ssid_index; i < wifi_playlist->count - 1; i++)
  329. {
  330. // Use strncpy to prevent buffer overflows and ensure null termination
  331. strncpy(wifi_playlist->ssids[i], wifi_playlist->ssids[i + 1], MAX_SSID_LENGTH - 1);
  332. wifi_playlist->ssids[i][MAX_SSID_LENGTH - 1] = '\0'; // Ensure null-termination
  333. strncpy(wifi_playlist->passwords[i], wifi_playlist->passwords[i + 1], MAX_SSID_LENGTH - 1);
  334. wifi_playlist->passwords[i][MAX_SSID_LENGTH - 1] = '\0'; // Ensure null-termination
  335. // Shift ssid_list
  336. ssid_list[i] = ssid_list[i + 1];
  337. }
  338. wifi_playlist->count--;
  339. // delete the last ssid and password
  340. wifi_playlist->ssids[wifi_playlist->count][0] = '\0';
  341. wifi_playlist->passwords[wifi_playlist->count][0] = '\0';
  342. // save the playlist to storage
  343. save_playlist(wifi_playlist);
  344. // re draw the saved submenu
  345. callback_redraw_submenu_saved(app);
  346. // switch back to the saved view
  347. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  348. return true;
  349. }
  350. return false;
  351. }
  352. static bool callback_set_html()
  353. {
  354. DialogsApp *dialogs = furi_record_open(RECORD_DIALOGS);
  355. DialogsFileBrowserOptions browser_options;
  356. dialog_file_browser_set_basic_options(&browser_options, ".js", NULL);
  357. browser_options.extension = "html";
  358. browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX;
  359. browser_options.skip_assets = true;
  360. browser_options.hide_dot_files = true;
  361. browser_options.icon = NULL;
  362. browser_options.hide_ext = false;
  363. FuriString *marauder_html_path = furi_string_alloc_set_str("/ext/apps_data/marauder/html");
  364. if (!marauder_html_path)
  365. {
  366. furi_record_close(RECORD_DIALOGS);
  367. return false;
  368. }
  369. if (dialog_file_browser_show(dialogs, marauder_html_path, marauder_html_path, &browser_options))
  370. {
  371. // Store the selected script file path
  372. const char *file_path = furi_string_get_cstr(marauder_html_path);
  373. save_char("ap_html_path", file_path);
  374. }
  375. furi_string_free(marauder_html_path);
  376. furi_record_close(RECORD_DIALOGS);
  377. return true;
  378. }
  379. static bool callback_run_ap_mode(void *context)
  380. {
  381. FlipWiFiApp *app = (FlipWiFiApp *)context;
  382. furi_check(app);
  383. if (!app->fhttp)
  384. {
  385. FURI_LOG_E(TAG, "FlipperHTTP is NULL");
  386. return false;
  387. }
  388. char ssid[64];
  389. if (!load_char("ap_ssid", ssid, sizeof(ssid)))
  390. {
  391. FURI_LOG_E(TAG, "Failed to load AP SSID");
  392. return false;
  393. }
  394. // clear response
  395. if (app->fhttp->last_response)
  396. snprintf(app->fhttp->last_response, RX_BUF_SIZE, "%s", "");
  397. char stat_command[128];
  398. snprintf(stat_command, sizeof(stat_command), "[WIFI/AP]{\"ssid\":\"%s\"}", ssid);
  399. if (!flipper_http_send_data(app->fhttp, stat_command))
  400. {
  401. FURI_LOG_E(TAG, "Failed to start AP mode");
  402. return false;
  403. }
  404. Loading *loading = loading_alloc();
  405. int32_t loading_view_id = 87654321; // Random ID
  406. view_dispatcher_add_view(app->view_dispatcher, loading_view_id, loading_get_view(loading));
  407. view_dispatcher_switch_to_view(app->view_dispatcher, loading_view_id);
  408. while (app->fhttp->last_response == NULL || strlen(app->fhttp->last_response) == 0)
  409. {
  410. furi_delay_ms(100);
  411. }
  412. // check success (if [AP/CONNECTED] is in the response)
  413. if (strstr(app->fhttp->last_response, "[AP/CONNECTED]") != NULL)
  414. {
  415. // send the HTML file
  416. char html_path[128];
  417. if (!load_char("ap_html_path", html_path, sizeof(html_path)))
  418. {
  419. FURI_LOG_E(TAG, "Failed to load HTML path");
  420. return false;
  421. }
  422. flipper_http_send_data(app->fhttp, "[WIFI/AP/UPDATE]");
  423. furi_delay_ms(1000);
  424. FuriString *html_content = flipper_http_load_from_file(html_path);
  425. if (html_content == NULL || furi_string_size(html_content) == 0)
  426. {
  427. FURI_LOG_E(TAG, "Failed to load HTML file");
  428. if (html_content)
  429. furi_string_free(html_content);
  430. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  431. view_dispatcher_remove_view(app->view_dispatcher, loading_view_id);
  432. loading_free(loading);
  433. loading = NULL;
  434. return false;
  435. }
  436. furi_string_cat_str(html_content, "\n");
  437. const char *send_buffer = furi_string_get_cstr(html_content);
  438. const size_t send_buffer_size = furi_string_size(html_content);
  439. app->fhttp->state = SENDING;
  440. size_t offset = 0;
  441. while (offset < send_buffer_size)
  442. {
  443. size_t chunk_size = send_buffer_size - offset > 512 ? 512 : send_buffer_size - offset;
  444. furi_hal_serial_tx(app->fhttp->serial_handle, (const uint8_t *)(send_buffer + offset), chunk_size);
  445. offset += chunk_size;
  446. furi_delay_ms(50); // cant go faster than this, no matter the chunk size
  447. }
  448. // send the [WIFI/AP/UPDATE/END] command
  449. flipper_http_send_data(app->fhttp, "[WIFI/AP/UPDATE/END]");
  450. app->fhttp->state = IDLE;
  451. furi_string_free(html_content);
  452. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  453. view_dispatcher_remove_view(app->view_dispatcher, loading_view_id);
  454. loading_free(loading);
  455. loading = NULL;
  456. return true;
  457. }
  458. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  459. view_dispatcher_remove_view(app->view_dispatcher, loading_view_id);
  460. loading_free(loading);
  461. loading = NULL;
  462. return false;
  463. }
  464. void callback_timer_callback(void *context)
  465. {
  466. furi_check(context, "callback_timer_callback: Context is NULL");
  467. FlipWiFiApp *app = (FlipWiFiApp *)context;
  468. view_dispatcher_send_custom_event(app->view_dispatcher, FlipWiFiCustomEventAP);
  469. }
  470. static size_t last_response_len = 0;
  471. static void update_text_box(FlipWiFiApp *app)
  472. {
  473. furi_check(app, "FlipWiFiApp is NULL");
  474. furi_check(app->textbox, "Text_box is NULL");
  475. if (app->fhttp)
  476. {
  477. if (!app->fhttp->last_response_str || furi_string_size(app->fhttp->last_response_str) == 0)
  478. {
  479. text_box_reset(app->textbox);
  480. text_box_set_focus(app->textbox, TextBoxFocusEnd);
  481. text_box_set_font(app->textbox, TextBoxFontText);
  482. text_box_set_text(app->textbox, "Connected... please wait");
  483. }
  484. else if (furi_string_size(app->fhttp->last_response_str) != last_response_len)
  485. {
  486. text_box_reset(app->textbox);
  487. text_box_set_focus(app->textbox, TextBoxFocusEnd);
  488. text_box_set_font(app->textbox, TextBoxFontText);
  489. last_response_len = furi_string_size(app->fhttp->last_response_str);
  490. text_box_set_text(app->textbox, furi_string_get_cstr(app->fhttp->last_response_str));
  491. }
  492. }
  493. }
  494. static void callback_loader_process_callback(void *context)
  495. {
  496. FlipWiFiApp *app = (FlipWiFiApp *)context;
  497. furi_check(app, "FlipWiFiApp is NULL");
  498. update_text_box(app);
  499. }
  500. static bool callback_custom_event_callback(void *context, uint32_t index)
  501. {
  502. furi_check(context, "callback_custom_event_callback: Context is NULL");
  503. switch (index)
  504. {
  505. case FlipWiFiCustomEventAP:
  506. callback_loader_process_callback(context);
  507. return true;
  508. default:
  509. FURI_LOG_E(TAG, "callback_custom_event_callback. Unknown index: %ld", index);
  510. return false;
  511. }
  512. }
  513. // scan for wifi ad parse the results
  514. static bool callback_scan(FlipperHTTP *fhttp)
  515. {
  516. if (!fhttp)
  517. {
  518. FURI_LOG_E(TAG, "FlipperHTTP is NULL");
  519. return false;
  520. }
  521. // storage setup
  522. Storage *storage = furi_record_open(RECORD_STORAGE);
  523. snprintf(fhttp->file_path, sizeof(fhttp->file_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_wifi/data/scan.txt");
  524. storage_simply_remove_recursive(storage, fhttp->file_path); // ensure the file is empty
  525. // ensure flip_wifi directory is there
  526. char directory_path[128];
  527. snprintf(directory_path, sizeof(directory_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_wifi");
  528. storage_common_mkdir(storage, directory_path);
  529. snprintf(directory_path, sizeof(directory_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_wifi/data");
  530. storage_common_mkdir(storage, directory_path);
  531. furi_record_close(RECORD_STORAGE);
  532. fhttp->just_started = true;
  533. fhttp->save_received_data = true;
  534. FURI_LOG_I(TAG, "callback_scan: Sending scan command");
  535. return flipper_http_send_command(fhttp, HTTP_CMD_SCAN);
  536. }
  537. static bool callback_handle_scan(FlipperHTTP *fhttp, void *context)
  538. {
  539. FURI_LOG_I(TAG, "callback_handle_scan1");
  540. FlipWiFiApp *app = (FlipWiFiApp *)context;
  541. furi_check(app);
  542. FURI_LOG_I(TAG, "callback_handle_scan2");
  543. if (!fhttp || !context)
  544. {
  545. FURI_LOG_E(TAG, "FlipperHTTP or context is NULL");
  546. return false;
  547. }
  548. // load the received data from the saved file
  549. FuriString *scan_data = flipper_http_load_from_file(fhttp->file_path);
  550. if (scan_data == NULL)
  551. {
  552. FURI_LOG_E(TAG, "Failed to load received data from file.");
  553. return false;
  554. }
  555. uint8_t ssid_count = 0;
  556. for (uint8_t i = 0; i < MAX_SCAN_NETWORKS; i++)
  557. {
  558. char *ssid_item = get_json_array_value("networks", i, furi_string_get_cstr(scan_data));
  559. if (ssid_item == NULL)
  560. {
  561. // end of the list
  562. break;
  563. }
  564. ssid_list[i] = malloc(MAX_SSID_LENGTH);
  565. if (ssid_list[i] == NULL)
  566. {
  567. FURI_LOG_E(TAG, "Failed to allocate memory for SSID");
  568. furi_string_free(scan_data);
  569. return false;
  570. }
  571. snprintf(ssid_list[i], MAX_SSID_LENGTH, "%s", ssid_item);
  572. free(ssid_item);
  573. ssid_count++;
  574. }
  575. // Add each SSID as a submenu item
  576. submenu_reset(app->submenu_wifi);
  577. submenu_set_header(app->submenu_wifi, "WiFi Nearby");
  578. for (uint8_t i = 0; i < ssid_count; i++)
  579. {
  580. char *ssid_item = ssid_list[i];
  581. if (ssid_item == NULL)
  582. {
  583. // end of the list
  584. break;
  585. }
  586. char ssid[64];
  587. snprintf(ssid, sizeof(ssid), "%s", ssid_item);
  588. submenu_add_item(app->submenu_wifi, ssid, FlipWiFiSubmenuIndexWiFiScanStart + i, callback_submenu_choices, app);
  589. }
  590. furi_string_free(scan_data);
  591. FURI_LOG_I(TAG, "Scan completed. Found %d networks.", ssid_count);
  592. return true;
  593. }
  594. void callback_submenu_choices(void *context, uint32_t index)
  595. {
  596. FlipWiFiApp *app = (FlipWiFiApp *)context;
  597. furi_check(app);
  598. switch (index)
  599. {
  600. case FlipWiFiSubmenuIndexWiFiScan:
  601. free_all(app);
  602. if (!alloc_submenus(app, FlipWiFiViewSubmenuScan))
  603. {
  604. easy_flipper_dialog("[ERROR]", "Failed to allocate submenus for WiFi Scan");
  605. return;
  606. }
  607. app->fhttp = flipper_http_alloc();
  608. if (!app->fhttp)
  609. {
  610. FURI_LOG_E(TAG, "Failed to allocate FlipperHTTP");
  611. easy_flipper_dialog("Error", "Failed to start UART.\nUART is likely busy or device\nis not connected.");
  612. return;
  613. }
  614. if (callback_scan(app->fhttp))
  615. {
  616. furi_delay_ms(100); // wait for the command to be sent
  617. // wait for the scan to complete
  618. Loading *loading = loading_alloc();
  619. int32_t loading_view_id = 87654321; // Random ID
  620. view_dispatcher_add_view(app->view_dispatcher, loading_view_id, loading_get_view(loading));
  621. view_dispatcher_switch_to_view(app->view_dispatcher, loading_view_id);
  622. while (app->fhttp->state != IDLE)
  623. {
  624. furi_delay_ms(100);
  625. }
  626. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  627. if (!callback_handle_scan(app->fhttp, app))
  628. {
  629. FURI_LOG_E(TAG, "Failed to handle scan");
  630. easy_flipper_dialog("[ERROR]", "Failed to handle scan");
  631. return;
  632. }
  633. view_dispatcher_remove_view(app->view_dispatcher, loading_view_id);
  634. loading_free(loading);
  635. loading = NULL;
  636. flipper_http_free(app->fhttp);
  637. }
  638. else
  639. {
  640. flipper_http_free(app->fhttp);
  641. easy_flipper_dialog("[ERROR]", "Failed to scan for WiFi networks");
  642. return;
  643. }
  644. break;
  645. case FlipWiFiSubmenuIndexWiFiDeauth:
  646. free_all(app);
  647. if (!alloc_text_inputs(app, FlipWiFiViewTextInputDeauth))
  648. {
  649. FURI_LOG_E(TAG, "Failed to allocate text input for WiFi Saved Add Password");
  650. return;
  651. }
  652. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewTextInput);
  653. break;
  654. case FlipWiFiSubmenuIndexWiFiSaved:
  655. free_all(app);
  656. if (!alloc_submenus(app, FlipWiFiViewSubmenuSaved))
  657. {
  658. FURI_LOG_E(TAG, "Failed to allocate submenus for WiFi Saved");
  659. return;
  660. }
  661. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  662. break;
  663. case FlipWiFiSubmenuIndexAbout:
  664. free_all(app);
  665. if (!alloc_widgets(app, FlipWiFiViewAbout))
  666. {
  667. FURI_LOG_E(TAG, "Failed to allocate widget for About");
  668. return;
  669. }
  670. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewAbout);
  671. break;
  672. case FlipWiFiSubmenuIndexWiFiSavedAddSSID:
  673. free_text_inputs(app);
  674. if (!alloc_text_inputs(app, FlipWiFiViewTextInputSavedAddSSID))
  675. {
  676. FURI_LOG_E(TAG, "Failed to allocate text input for WiFi Saved Add Password");
  677. return;
  678. }
  679. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewTextInput);
  680. break;
  681. case FlipWiFiSubmenuIndexWiFiAP:
  682. free_all(app);
  683. if (!alloc_submenus(app, FlipWiFiViewSubmenuAP))
  684. {
  685. FURI_LOG_E(TAG, "Failed to allocate submenus for APs");
  686. return;
  687. }
  688. app->fhttp = flipper_http_alloc();
  689. if (!app->fhttp)
  690. {
  691. FURI_LOG_E(TAG, "Failed to allocate FlipperHTTP");
  692. easy_flipper_dialog("Error", "Failed to start UART.\nUART is likely busy or device\nis not connected.");
  693. return;
  694. }
  695. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  696. break;
  697. case FlipWiFiSubmenuIndexWiFiAPStart:
  698. // start AP
  699. view_dispatcher_set_custom_event_callback(app->view_dispatcher, callback_custom_event_callback);
  700. // send to AP View to see the responses
  701. if (!callback_run_ap_mode(app))
  702. {
  703. easy_flipper_dialog("[ERROR]", "Failed to start AP mode");
  704. return;
  705. }
  706. free_text_box(app);
  707. if (!alloc_text_box(app))
  708. {
  709. FURI_LOG_E(TAG, "Failed to allocate text box");
  710. return;
  711. }
  712. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewWiFiAP);
  713. break;
  714. case FlipWiFiSubmenuIndexWiFiAPSetSSID:
  715. // set SSID
  716. free_text_inputs(app);
  717. if (!alloc_text_inputs(app, FlipWiFiSubmenuIndexWiFiAPSetSSID))
  718. {
  719. FURI_LOG_E(TAG, "Failed to allocate text input for Fast Command");
  720. return;
  721. }
  722. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewTextInput);
  723. break;
  724. case FlipWiFiSubmenuIndexWiFiAPSetHTML:
  725. // set HTML
  726. callback_set_html();
  727. break;
  728. case FlipWiFiSubmenuIndexCommands:
  729. free_all(app);
  730. if (!alloc_submenus(app, FlipWiFiViewSubmenuCommands))
  731. {
  732. FURI_LOG_E(TAG, "Failed to allocate submenus for Commands");
  733. return;
  734. }
  735. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  736. break;
  737. case FlipWiFiSubmenuIndexFastCommandStart ... FlipWiFiSubmenuIndexFastCommandStart + 4:
  738. // initialize uart
  739. if (!app->fhttp)
  740. {
  741. app->fhttp = flipper_http_alloc();
  742. if (!app->fhttp)
  743. {
  744. FURI_LOG_E(TAG, "Failed to allocate FlipperHTTP");
  745. easy_flipper_dialog("Error", "Failed to start UART.\nUART is likely busy or device\nis not connected.");
  746. return;
  747. }
  748. }
  749. // Handle fast commands
  750. switch (index)
  751. {
  752. case FlipWiFiSubmenuIndexFastCommandStart + 0:
  753. // CUSTOM - send to text input and return
  754. free_text_inputs(app);
  755. if (!alloc_text_inputs(app, FlipWiFiSubmenuIndexFastCommandStart))
  756. {
  757. FURI_LOG_E(TAG, "Failed to allocate text input for Fast Command");
  758. return;
  759. }
  760. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewTextInput);
  761. return;
  762. case FlipWiFiSubmenuIndexFastCommandStart + 1:
  763. // PING
  764. flipper_http_send_command(app->fhttp, HTTP_CMD_PING);
  765. break;
  766. case FlipWiFiSubmenuIndexFastCommandStart + 2:
  767. // LIST
  768. flipper_http_send_command(app->fhttp, HTTP_CMD_LIST_COMMANDS);
  769. break;
  770. case FlipWiFiSubmenuIndexFastCommandStart + 3:
  771. // IP/ADDRESS
  772. flipper_http_send_command(app->fhttp, HTTP_CMD_IP_ADDRESS);
  773. break;
  774. case FlipWiFiSubmenuIndexFastCommandStart + 4:
  775. // WIFI/IP
  776. flipper_http_send_command(app->fhttp, HTTP_CMD_IP_WIFI);
  777. break;
  778. default:
  779. break;
  780. }
  781. while (app->fhttp->last_response == NULL || strlen(app->fhttp->last_response) == 0)
  782. {
  783. // Wait for the response
  784. furi_delay_ms(100);
  785. }
  786. if (app->fhttp->last_response != NULL)
  787. {
  788. char response[100];
  789. snprintf(response, sizeof(response), "%s", app->fhttp->last_response);
  790. easy_flipper_dialog("", response);
  791. }
  792. flipper_http_free(app->fhttp);
  793. break;
  794. case 100 ... 199:
  795. ssid_index = index - FlipWiFiSubmenuIndexWiFiScanStart;
  796. free_views(app);
  797. if (!alloc_views(app, FlipWiFiViewWiFiScan))
  798. {
  799. FURI_LOG_E(TAG, "Failed to allocate views for WiFi Scan");
  800. return;
  801. }
  802. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewGeneric);
  803. break;
  804. case 200 ... 299:
  805. ssid_index = index - FlipWiFiSubmenuIndexWiFiSavedStart;
  806. free_views(app);
  807. snprintf(current_ssid, sizeof(current_ssid), "%s", wifi_playlist->ssids[ssid_index]);
  808. snprintf(current_password, sizeof(current_password), "%s", wifi_playlist->passwords[ssid_index]);
  809. if (!alloc_views(app, FlipWiFiViewWiFiSaved))
  810. {
  811. FURI_LOG_E(TAG, "Failed to allocate views for WiFi Saved");
  812. return;
  813. }
  814. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewGeneric);
  815. break;
  816. default:
  817. break;
  818. }
  819. }
  820. void callback_text_updated_password_scan(void *context)
  821. {
  822. FlipWiFiApp *app = (FlipWiFiApp *)context;
  823. furi_check(app);
  824. // Store the entered text with buffer size limit
  825. strncpy(app->uart_text_input_buffer, app->uart_text_input_temp_buffer, app->uart_text_input_buffer_size - 1);
  826. // Ensure null-termination
  827. app->uart_text_input_buffer[app->uart_text_input_buffer_size - 1] = '\0';
  828. if (!alloc_playlist(app))
  829. {
  830. FURI_LOG_E(TAG, "Failed to allocate playlist");
  831. return;
  832. }
  833. // Ensure ssid_index is valid
  834. if (ssid_index >= MAX_SCAN_NETWORKS)
  835. {
  836. FURI_LOG_E(TAG, "Invalid ssid_index: %ld", ssid_index);
  837. return;
  838. }
  839. // Check if there's space in the playlist
  840. if (wifi_playlist->count >= MAX_SAVED_NETWORKS)
  841. {
  842. FURI_LOG_E(TAG, "Playlist is full. Cannot add more entries.");
  843. return;
  844. }
  845. // Add the SSID and password to the playlist
  846. snprintf(wifi_playlist->ssids[wifi_playlist->count], MAX_SSID_LENGTH, "%s", ssid_list[ssid_index]);
  847. snprintf(wifi_playlist->passwords[wifi_playlist->count], MAX_SSID_LENGTH, "%s", app->uart_text_input_buffer);
  848. wifi_playlist->count++;
  849. // Save the updated playlist to storage
  850. save_playlist(wifi_playlist);
  851. // Redraw the submenu to reflect changes
  852. callback_redraw_submenu_saved(app);
  853. // Switch back to the scan view
  854. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  855. }
  856. void callback_text_updated_password_saved(void *context)
  857. {
  858. FlipWiFiApp *app = (FlipWiFiApp *)context;
  859. furi_check(app);
  860. // store the entered text
  861. strncpy(app->uart_text_input_buffer, app->uart_text_input_temp_buffer, app->uart_text_input_buffer_size);
  862. // Ensure null-termination
  863. app->uart_text_input_buffer[app->uart_text_input_buffer_size - 1] = '\0';
  864. // update the password_saved in the playlist
  865. snprintf(wifi_playlist->passwords[ssid_index], MAX_SSID_LENGTH, app->uart_text_input_buffer);
  866. // save the playlist to storage
  867. save_playlist(wifi_playlist);
  868. // switch to back to the saved view
  869. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  870. }
  871. void callback_text_updated_add_ssid(void *context)
  872. {
  873. FlipWiFiApp *app = (FlipWiFiApp *)context;
  874. furi_check(app);
  875. // check if empty
  876. if (strlen(app->uart_text_input_temp_buffer) == 0)
  877. {
  878. easy_flipper_dialog("[ERROR]", "SSID cannot be empty");
  879. return;
  880. }
  881. // store the entered text
  882. strncpy(app->uart_text_input_buffer, app->uart_text_input_temp_buffer, app->uart_text_input_buffer_size);
  883. // Ensure null-termination
  884. app->uart_text_input_buffer[app->uart_text_input_buffer_size - 1] = '\0';
  885. save_char("wifi-ssid", app->uart_text_input_buffer);
  886. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenuMain);
  887. uart_text_input_reset(app->uart_text_input);
  888. uart_text_input_set_header_text(app->uart_text_input, "Enter Password");
  889. app->uart_text_input_buffer_size = MAX_SSID_LENGTH;
  890. free(app->uart_text_input_buffer);
  891. free(app->uart_text_input_temp_buffer);
  892. easy_flipper_set_buffer(&app->uart_text_input_buffer, app->uart_text_input_buffer_size);
  893. easy_flipper_set_buffer(&app->uart_text_input_temp_buffer, app->uart_text_input_buffer_size);
  894. uart_text_input_set_result_callback(app->uart_text_input, callback_text_updated_add_password, app, app->uart_text_input_temp_buffer, app->uart_text_input_buffer_size, false);
  895. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewTextInput);
  896. }
  897. void callback_text_updated_add_password(void *context)
  898. {
  899. FlipWiFiApp *app = (FlipWiFiApp *)context;
  900. furi_check(app);
  901. // check if empty
  902. if (strlen(app->uart_text_input_temp_buffer) == 0)
  903. {
  904. easy_flipper_dialog("[ERROR]", "Password cannot be empty");
  905. return;
  906. }
  907. // store the entered text
  908. strncpy(app->uart_text_input_buffer, app->uart_text_input_temp_buffer, app->uart_text_input_buffer_size);
  909. // Ensure null-termination
  910. app->uart_text_input_buffer[app->uart_text_input_buffer_size - 1] = '\0';
  911. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenuMain);
  912. save_char("wifi-password", app->uart_text_input_buffer);
  913. char wifi_ssid[64];
  914. if (!load_char("wifi-ssid", wifi_ssid, sizeof(wifi_ssid)))
  915. {
  916. FURI_LOG_E(TAG, "Failed to load wifi ssid");
  917. return;
  918. }
  919. // add the SSID and password_scan to the playlist
  920. snprintf(wifi_playlist->ssids[wifi_playlist->count], MAX_SSID_LENGTH, wifi_ssid);
  921. snprintf(wifi_playlist->passwords[wifi_playlist->count], MAX_SSID_LENGTH, app->uart_text_input_buffer);
  922. wifi_playlist->count++;
  923. // save the playlist to storage
  924. save_playlist(wifi_playlist);
  925. callback_redraw_submenu_saved(app);
  926. // switch to back to the saved view
  927. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  928. }