callback.c 36 KB

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