From 0d2f0fce3696629c8a67e6484c9bf38630a96c85 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 16 Sep 2023 22:13:45 +0200 Subject: [PATCH] Update MAYHEM marauder (+ remove evil portal) --- .../esp32cam_evilportal/application.fam | 14 - .../esp32cam_evilportal/evil_portal_app.c | 133 - .../esp32cam_evilportal/evil_portal_app.h | 11 - .../esp32cam_evilportal/evil_portal_app_i.h | 61 - .../evil_portal_custom_event.h | 8 - .../esp32cam_evilportal/evil_portal_uart.c | 149 - .../esp32cam_evilportal/evil_portal_uart.h | 14 - .../helpers/evil_portal_storage.c | 117 - .../helpers/evil_portal_storage.h | 20 - .../icons/evil_portal_10px.png | Bin 156 -> 0 bytes .../scenes/evil_portal_scene.c | 30 - .../scenes/evil_portal_scene.h | 29 - .../scenes/evil_portal_scene_config.h | 2 - .../scenes/evil_portal_scene_console_output.c | 147 - .../scenes/evil_portal_scene_start.c | 130 - .../esp32cam_marauder_companion/LICENSE | 674 ++++ .../application.fam | 3 +- .../assets/Text_10x10.png | Bin 0 -> 158 bytes .../file/sequential_file.c | 46 + .../file/sequential_file.h | 15 + .../scenes/wifi_marauder_scene_config.h | 10 + .../wifi_marauder_scene_console_output.c | 108 +- .../scenes/wifi_marauder_scene_log_viewer.c | 4 +- ...ifi_marauder_scene_script_confirm_delete.c | 83 + .../scenes/wifi_marauder_scene_script_edit.c | 125 + .../wifi_marauder_scene_script_edit_list.c | 188 ++ .../wifi_marauder_scene_script_options.c | 111 + .../wifi_marauder_scene_script_select.c | 90 + .../wifi_marauder_scene_script_settings.c | 87 + .../wifi_marauder_scene_script_stage_add.c | 297 ++ .../wifi_marauder_scene_script_stage_edit.c | 203 ++ .../wifi_marauder_scene_sniffpmkid_options.c | 117 + .../scenes/wifi_marauder_scene_start.c | 89 +- .../scenes/wifi_marauder_scene_user_input.c | 157 + .../script/cJSON.c | 2743 +++++++++++++++++ .../script/cJSON.h | 321 ++ .../menu/wifi_marauder_script_stage_menu.c | 32 + .../menu/wifi_marauder_script_stage_menu.h | 42 + ...wifi_marauder_script_stage_menu_beaconap.c | 27 + ...fi_marauder_script_stage_menu_beaconlist.c | 59 + .../wifi_marauder_script_stage_menu_config.h | 14 + .../wifi_marauder_script_stage_menu_deauth.c | 27 + .../wifi_marauder_script_stage_menu_delay.c | 27 + .../wifi_marauder_script_stage_menu_exec.c | 30 + .../wifi_marauder_script_stage_menu_probe.c | 27 + .../wifi_marauder_script_stage_menu_scan.c | 93 + .../wifi_marauder_script_stage_menu_select.c | 95 + ...i_marauder_script_stage_menu_sniffbeacon.c | 27 + ...i_marauder_script_stage_menu_sniffdeauth.c | 27 + ...wifi_marauder_script_stage_menu_sniffesp.c | 27 + ...fi_marauder_script_stage_menu_sniffpmkid.c | 118 + ...wifi_marauder_script_stage_menu_sniffpwn.c | 27 + ...wifi_marauder_script_stage_menu_sniffraw.c | 27 + .../script/wifi_marauder_script.c | 962 ++++++ .../script/wifi_marauder_script.h | 258 ++ .../script/wifi_marauder_script_executor.c | 324 ++ .../script/wifi_marauder_script_executor.h | 6 + .../script/wifi_marauder_script_worker.c | 75 + .../script/wifi_marauder_script_worker.h | 43 + .../wifi_marauder_app.c | 13 + .../wifi_marauder_app.h | 2 +- .../wifi_marauder_app_i.h | 44 +- .../wifi_marauder_custom_event.h | 4 +- .../wifi_marauder_pcap.c | 64 - .../wifi_marauder_pcap.h | 20 - .../wifi_marauder_validators.c | 57 + .../wifi_marauder_validators.h | 21 + 67 files changed, 7957 insertions(+), 998 deletions(-) delete mode 100644 applications/external/esp32cam_evilportal/application.fam delete mode 100644 applications/external/esp32cam_evilportal/evil_portal_app.c delete mode 100644 applications/external/esp32cam_evilportal/evil_portal_app.h delete mode 100644 applications/external/esp32cam_evilportal/evil_portal_app_i.h delete mode 100644 applications/external/esp32cam_evilportal/evil_portal_custom_event.h delete mode 100644 applications/external/esp32cam_evilportal/evil_portal_uart.c delete mode 100644 applications/external/esp32cam_evilportal/evil_portal_uart.h delete mode 100644 applications/external/esp32cam_evilportal/helpers/evil_portal_storage.c delete mode 100644 applications/external/esp32cam_evilportal/helpers/evil_portal_storage.h delete mode 100644 applications/external/esp32cam_evilportal/icons/evil_portal_10px.png delete mode 100644 applications/external/esp32cam_evilportal/scenes/evil_portal_scene.c delete mode 100644 applications/external/esp32cam_evilportal/scenes/evil_portal_scene.h delete mode 100644 applications/external/esp32cam_evilportal/scenes/evil_portal_scene_config.h delete mode 100644 applications/external/esp32cam_evilportal/scenes/evil_portal_scene_console_output.c delete mode 100644 applications/external/esp32cam_evilportal/scenes/evil_portal_scene_start.c create mode 100644 applications/external/esp32cam_marauder_companion/LICENSE create mode 100644 applications/external/esp32cam_marauder_companion/assets/Text_10x10.png create mode 100644 applications/external/esp32cam_marauder_companion/file/sequential_file.c create mode 100644 applications/external/esp32cam_marauder_companion/file/sequential_file.h create mode 100644 applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_confirm_delete.c create mode 100644 applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_edit.c create mode 100644 applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_edit_list.c create mode 100644 applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_options.c create mode 100644 applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_select.c create mode 100644 applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_settings.c create mode 100644 applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_stage_add.c create mode 100644 applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_stage_edit.c create mode 100644 applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_sniffpmkid_options.c create mode 100644 applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_user_input.c create mode 100644 applications/external/esp32cam_marauder_companion/script/cJSON.c create mode 100644 applications/external/esp32cam_marauder_companion/script/cJSON.h create mode 100644 applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu.c create mode 100644 applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu.h create mode 100644 applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconap.c create mode 100644 applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconlist.c create mode 100644 applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_config.h create mode 100644 applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_deauth.c create mode 100644 applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_delay.c create mode 100644 applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_exec.c create mode 100644 applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_probe.c create mode 100644 applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_scan.c create mode 100644 applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_select.c create mode 100644 applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffbeacon.c create mode 100644 applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffdeauth.c create mode 100644 applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffesp.c create mode 100644 applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpmkid.c create mode 100644 applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpwn.c create mode 100644 applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffraw.c create mode 100644 applications/external/esp32cam_marauder_companion/script/wifi_marauder_script.c create mode 100644 applications/external/esp32cam_marauder_companion/script/wifi_marauder_script.h create mode 100644 applications/external/esp32cam_marauder_companion/script/wifi_marauder_script_executor.c create mode 100644 applications/external/esp32cam_marauder_companion/script/wifi_marauder_script_executor.h create mode 100644 applications/external/esp32cam_marauder_companion/script/wifi_marauder_script_worker.c create mode 100644 applications/external/esp32cam_marauder_companion/script/wifi_marauder_script_worker.h delete mode 100644 applications/external/esp32cam_marauder_companion/wifi_marauder_pcap.c delete mode 100644 applications/external/esp32cam_marauder_companion/wifi_marauder_pcap.h create mode 100644 applications/external/esp32cam_marauder_companion/wifi_marauder_validators.c create mode 100644 applications/external/esp32cam_marauder_companion/wifi_marauder_validators.h diff --git a/applications/external/esp32cam_evilportal/application.fam b/applications/external/esp32cam_evilportal/application.fam deleted file mode 100644 index d842ddc79..000000000 --- a/applications/external/esp32cam_evilportal/application.fam +++ /dev/null @@ -1,14 +0,0 @@ -App( - appid="mayhem_evil_portal", - name="[MAYHEM] Evil Portal", - apptype=FlipperAppType.EXTERNAL, - entry_point="evil_portal_app", - cdefines=["APP_EVIL_PORTAL"], - requires=["gui"], - stack_size=1 * 1024, - fap_icon="icons/evil_portal_10px.png", - fap_category="GPIO", - fap_description="ESP32-CAM evil portal. When users try to connect to this access point they will be served a fake login screen. User credentials are sent to the Flipper and logged on the SD card. [Unplug the USB cable to test with Mayhem]", - fap_author="bigbrodude6119", - fap_weburl="https://github.com/bigbrodude6119/flipper-zero-evil-portal", -) diff --git a/applications/external/esp32cam_evilportal/evil_portal_app.c b/applications/external/esp32cam_evilportal/evil_portal_app.c deleted file mode 100644 index c715b6de7..000000000 --- a/applications/external/esp32cam_evilportal/evil_portal_app.c +++ /dev/null @@ -1,133 +0,0 @@ -#include "evil_portal_app_i.h" -#include "helpers/evil_portal_storage.h" - -#include -#include - -static bool evil_portal_app_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - Evil_PortalApp* app = context; - return scene_manager_handle_custom_event(app->scene_manager, event); -} - -static bool evil_portal_app_back_event_callback(void* context) { - furi_assert(context); - Evil_PortalApp* app = context; - return scene_manager_handle_back_event(app->scene_manager); -} - -static void evil_portal_app_tick_event_callback(void* context) { - furi_assert(context); - Evil_PortalApp* app = context; - scene_manager_handle_tick_event(app->scene_manager); -} - -Evil_PortalApp* evil_portal_app_alloc() { - Evil_PortalApp* app = malloc(sizeof(Evil_PortalApp)); - - app->sent_html = false; - app->sent_ap = false; - app->sent_reset = false; - app->has_command_queue = false; - app->command_index = 0; - app->portal_logs = furi_string_alloc(); - - app->gui = furi_record_open(RECORD_GUI); - - app->view_dispatcher = view_dispatcher_alloc(); - app->scene_manager = scene_manager_alloc(&evil_portal_scene_handlers, app); - view_dispatcher_enable_queue(app->view_dispatcher); - view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - - view_dispatcher_set_custom_event_callback( - app->view_dispatcher, evil_portal_app_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - app->view_dispatcher, evil_portal_app_back_event_callback); - view_dispatcher_set_tick_event_callback( - app->view_dispatcher, evil_portal_app_tick_event_callback, 100); - - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - - app->var_item_list = variable_item_list_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - Evil_PortalAppViewVarItemList, - variable_item_list_get_view(app->var_item_list)); - - for(int i = 0; i < NUM_MENU_ITEMS; ++i) { - app->selected_option_index[i] = 0; - } - - app->text_box = text_box_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, Evil_PortalAppViewConsoleOutput, text_box_get_view(app->text_box)); - app->text_box_store = furi_string_alloc(); - furi_string_reserve(app->text_box_store, EVIL_PORTAL_TEXT_BOX_STORE_SIZE); - - scene_manager_next_scene(app->scene_manager, Evil_PortalSceneStart); - - return app; -} - -void evil_portal_app_free(Evil_PortalApp* app) { - // save latest logs - if(furi_string_utf8_length(app->portal_logs) > 0) { - write_logs(app->portal_logs); - furi_string_free(app->portal_logs); - } - - // Send reset event to dev board - evil_portal_uart_tx((uint8_t*)(RESET_CMD), strlen(RESET_CMD)); - evil_portal_uart_tx((uint8_t*)("\n"), 1); - - furi_assert(app); - - // Views - view_dispatcher_remove_view(app->view_dispatcher, Evil_PortalAppViewVarItemList); - view_dispatcher_remove_view(app->view_dispatcher, Evil_PortalAppViewConsoleOutput); - - text_box_free(app->text_box); - furi_string_free(app->text_box_store); - - // View dispatcher - view_dispatcher_free(app->view_dispatcher); - scene_manager_free(app->scene_manager); - - evil_portal_uart_free(app->uart); - - // Close records - furi_record_close(RECORD_GUI); - - free(app); -} - -int32_t evil_portal_app(void* p) { - UNUSED(p); - - // Enable uart listener - furi_hal_console_disable(); - furi_hal_uart_set_br(UART_CH, BAUDRATE); // TODO: Clean this - //furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, evil_portal_uart_on_irq_cb, app); - - furi_hal_power_disable_external_3_3v(); - furi_hal_power_disable_otg(); - furi_delay_ms(200); - furi_hal_power_enable_external_3_3v(); - furi_hal_power_enable_otg(); - for(int i = 0; i < 2; i++) { - furi_delay_ms(500); - furi_hal_uart_tx(UART_CH, (uint8_t[1]){'e'}, 1); - } - furi_delay_ms(1); - Evil_PortalApp* evil_portal_app = evil_portal_app_alloc(); - - evil_portal_app->uart = evil_portal_uart_init(evil_portal_app); - - view_dispatcher_run(evil_portal_app->view_dispatcher); - - evil_portal_app_free(evil_portal_app); - - furi_hal_power_disable_otg(); - - return 0; -} diff --git a/applications/external/esp32cam_evilportal/evil_portal_app.h b/applications/external/esp32cam_evilportal/evil_portal_app.h deleted file mode 100644 index 65c047ea5..000000000 --- a/applications/external/esp32cam_evilportal/evil_portal_app.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct Evil_PortalApp Evil_PortalApp; - -#ifdef __cplusplus -} -#endif diff --git a/applications/external/esp32cam_evilportal/evil_portal_app_i.h b/applications/external/esp32cam_evilportal/evil_portal_app_i.h deleted file mode 100644 index e0c3e36d1..000000000 --- a/applications/external/esp32cam_evilportal/evil_portal_app_i.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -#include "evil_portal_app.h" -#include "evil_portal_custom_event.h" -#include "evil_portal_uart.h" -#include "scenes/evil_portal_scene.h" - -#include -#include -#include -#include -#include - -#define NUM_MENU_ITEMS (4) - -#define EVIL_PORTAL_TEXT_BOX_STORE_SIZE (4096) -#define UART_CH (FuriHalUartIdUSART1) -#define BAUDRATE (230400) - -#define SET_HTML_CMD "sethtml" -#define SET_AP_CMD "setap" -#define RESET_CMD "reset" - -struct Evil_PortalApp { - Gui* gui; - ViewDispatcher* view_dispatcher; - SceneManager* scene_manager; - - FuriString* portal_logs; - const char* command_queue[1]; - int command_index; - bool has_command_queue; - - FuriString* text_box_store; - size_t text_box_store_strlen; - TextBox* text_box; - - VariableItemList* var_item_list; - Evil_PortalUart* uart; - - int selected_menu_index; - int selected_option_index[NUM_MENU_ITEMS]; - const char* selected_tx_string; - bool is_command; - bool is_custom_tx_string; - bool focus_console_start; - bool show_stopscan_tip; - bool sent_ap; - bool sent_html; - bool sent_reset; - //int BAUDRATE; - - uint8_t* index_html; - uint8_t* ap_name; -}; - -typedef enum { - Evil_PortalAppViewVarItemList, - Evil_PortalAppViewConsoleOutput, - Evil_PortalAppViewStartPortal, -} Evil_PortalAppView; diff --git a/applications/external/esp32cam_evilportal/evil_portal_custom_event.h b/applications/external/esp32cam_evilportal/evil_portal_custom_event.h deleted file mode 100644 index a566ca62e..000000000 --- a/applications/external/esp32cam_evilportal/evil_portal_custom_event.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -typedef enum { - Evil_PortalEventRefreshConsoleOutput = 0, - Evil_PortalEventStartConsole, - Evil_PortalEventStartKeyboard, - Evil_PortalEventStartPortal, -} Evil_PortalCustomEvent; diff --git a/applications/external/esp32cam_evilportal/evil_portal_uart.c b/applications/external/esp32cam_evilportal/evil_portal_uart.c deleted file mode 100644 index 5c1c104a7..000000000 --- a/applications/external/esp32cam_evilportal/evil_portal_uart.c +++ /dev/null @@ -1,149 +0,0 @@ -#include "evil_portal_app_i.h" -#include "evil_portal_uart.h" -#include "helpers/evil_portal_storage.h" - -struct Evil_PortalUart { - Evil_PortalApp* app; - FuriThread* rx_thread; - FuriStreamBuffer* rx_stream; - uint8_t rx_buf[RX_BUF_SIZE + 1]; - void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context); -}; - -typedef enum { - WorkerEvtStop = (1 << 0), - WorkerEvtRxDone = (1 << 1), -} WorkerEvtFlags; - -void evil_portal_uart_set_handle_rx_data_cb( - Evil_PortalUart* uart, - void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context)) { - furi_assert(uart); - uart->handle_rx_data_cb = handle_rx_data_cb; -} - -#define WORKER_ALL_RX_EVENTS (WorkerEvtStop | WorkerEvtRxDone) - -void evil_portal_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { - Evil_PortalUart* uart = (Evil_PortalUart*)context; - - if(ev == UartIrqEventRXNE) { - furi_stream_buffer_send(uart->rx_stream, &data, 1, 0); - furi_thread_flags_set(furi_thread_get_id(uart->rx_thread), WorkerEvtRxDone); - } -} - -static int32_t uart_worker(void* context) { - Evil_PortalUart* uart = (void*)context; - - while(1) { - uint32_t events = - furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever); - furi_check((events & FuriFlagError) == 0); - if(events & WorkerEvtStop) break; - if(events & WorkerEvtRxDone) { - size_t len = furi_stream_buffer_receive(uart->rx_stream, uart->rx_buf, RX_BUF_SIZE, 0); - - if(len > 0) { - if(uart->handle_rx_data_cb) { - uart->handle_rx_data_cb(uart->rx_buf, len, uart->app); - - if(uart->app->has_command_queue) { - if(uart->app->command_index < 1) { - if(0 == strncmp( - SET_AP_CMD, - uart->app->command_queue[uart->app->command_index], - strlen(SET_AP_CMD))) { - FuriString* out_data = furi_string_alloc(); - - furi_string_cat(out_data, "setap="); - furi_string_cat(out_data, (char*)uart->app->ap_name); - - evil_portal_uart_tx( - (uint8_t*)(furi_string_get_cstr(out_data)), - strlen(furi_string_get_cstr(out_data))); - evil_portal_uart_tx((uint8_t*)("\n"), 1); - - uart->app->sent_ap = true; - - free(out_data); - free(uart->app->ap_name); - } - - uart->app->command_index = 0; - uart->app->has_command_queue = false; - uart->app->command_queue[0] = ""; - } - } - - if(uart->app->sent_reset == false) { - furi_string_cat(uart->app->portal_logs, (char*)uart->rx_buf); - } - - if(furi_string_utf8_length(uart->app->portal_logs) > 4000) { - write_logs(uart->app->portal_logs); - furi_string_reset(uart->app->portal_logs); - } - } else { - uart->rx_buf[len] = '\0'; - if(uart->app->sent_reset == false) { - furi_string_cat(uart->app->portal_logs, (char*)uart->rx_buf); - } - - if(furi_string_utf8_length(uart->app->portal_logs) > 4000) { - write_logs(uart->app->portal_logs); - furi_string_reset(uart->app->portal_logs); - } - } - } - } - } - - furi_stream_buffer_free(uart->rx_stream); - - return 0; -} - -void evil_portal_uart_tx(uint8_t* data, size_t len) { - furi_hal_uart_tx(UART_CH, data, len); -} - -Evil_PortalUart* evil_portal_uart_init(Evil_PortalApp* app) { - Evil_PortalUart* uart = malloc(sizeof(Evil_PortalUart)); - uart->app = app; - // Init all rx stream and thread early to avoid crashes - uart->rx_stream = furi_stream_buffer_alloc(RX_BUF_SIZE, 1); - uart->rx_thread = furi_thread_alloc(); - furi_thread_set_name(uart->rx_thread, "Evil_PortalUartRxThread"); - furi_thread_set_stack_size(uart->rx_thread, 1024); - furi_thread_set_context(uart->rx_thread, uart); - furi_thread_set_callback(uart->rx_thread, uart_worker); - - furi_thread_start(uart->rx_thread); - - /*furi_hal_console_disable(); - if (app->BAUDRATE == 0) { - app->BAUDRATE = 115200; - } - furi_hal_uart_set_br(UART_CH, app->BAUDRATE); - furi_hal_uart_set_irq_cb(UART_CH, evil_portal_uart_on_irq_cb, uart);*/ - - furi_hal_console_disable(); - furi_hal_uart_set_br(UART_CH, BAUDRATE); - furi_hal_uart_set_irq_cb(UART_CH, evil_portal_uart_on_irq_cb, uart); - - return uart; -} - -void evil_portal_uart_free(Evil_PortalUart* uart) { - furi_assert(uart); - - furi_thread_flags_set(furi_thread_get_id(uart->rx_thread), WorkerEvtStop); - furi_thread_join(uart->rx_thread); - furi_thread_free(uart->rx_thread); - - furi_hal_uart_set_irq_cb(UART_CH, NULL, NULL); - furi_hal_console_enable(); - - free(uart); -} diff --git a/applications/external/esp32cam_evilportal/evil_portal_uart.h b/applications/external/esp32cam_evilportal/evil_portal_uart.h deleted file mode 100644 index d7980a8e2..000000000 --- a/applications/external/esp32cam_evilportal/evil_portal_uart.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "furi_hal.h" - -#define RX_BUF_SIZE (320) - -typedef struct Evil_PortalUart Evil_PortalUart; - -void evil_portal_uart_set_handle_rx_data_cb( - Evil_PortalUart* uart, - void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context)); -void evil_portal_uart_tx(uint8_t* data, size_t len); -Evil_PortalUart* evil_portal_uart_init(Evil_PortalApp* app); -void evil_portal_uart_free(Evil_PortalUart* uart); diff --git a/applications/external/esp32cam_evilportal/helpers/evil_portal_storage.c b/applications/external/esp32cam_evilportal/helpers/evil_portal_storage.c deleted file mode 100644 index b28c97afa..000000000 --- a/applications/external/esp32cam_evilportal/helpers/evil_portal_storage.c +++ /dev/null @@ -1,117 +0,0 @@ -#include "evil_portal_storage.h" - -static Storage* evil_portal_open_storage() { - return furi_record_open(RECORD_STORAGE); -} - -static void evil_portal_close_storage() { - furi_record_close(RECORD_STORAGE); -} - -void evil_portal_read_index_html(void* context) { - Evil_PortalApp* app = context; - Storage* storage = evil_portal_open_storage(); - FileInfo fi; - - if(storage_common_stat(storage, EVIL_PORTAL_INDEX_SAVE_PATH, &fi) == FSE_OK) { - File* index_html = storage_file_alloc(storage); - if(storage_file_open( - index_html, EVIL_PORTAL_INDEX_SAVE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { - app->index_html = malloc((size_t)fi.size); - uint8_t* buf_ptr = app->index_html; - size_t read = 0; - while(read < fi.size) { - size_t to_read = fi.size - read; - if(to_read > UINT16_MAX) to_read = UINT16_MAX; - uint16_t now_read = storage_file_read(index_html, buf_ptr, (uint16_t)to_read); - read += now_read; - buf_ptr += now_read; - } - free(buf_ptr); - } - storage_file_close(index_html); - storage_file_free(index_html); - } else { - char* html_error = "Evil portal
Unable to read the html file.
" - "Is the SD Card set up correctly?
See instructions @ " - "github.com/bigbrodude6119/flipper-zero-evil-portal
" - "Under the 'Install pre-built app on the flipper' section."; - app->index_html = (uint8_t*)html_error; - } - - evil_portal_close_storage(); -} - -void evil_portal_read_ap_name(void* context) { - Evil_PortalApp* app = context; - Storage* storage = evil_portal_open_storage(); - FileInfo fi; - - if(storage_common_stat(storage, EVIL_PORTAL_AP_SAVE_PATH, &fi) == FSE_OK) { - File* ap_name = storage_file_alloc(storage); - if(storage_file_open(ap_name, EVIL_PORTAL_AP_SAVE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { - app->ap_name = malloc((size_t)fi.size); - uint8_t* buf_ptr = app->ap_name; - size_t read = 0; - while(read < fi.size) { - size_t to_read = fi.size - read; - if(to_read > UINT16_MAX) to_read = UINT16_MAX; - uint16_t now_read = storage_file_read(ap_name, buf_ptr, (uint16_t)to_read); - read += now_read; - buf_ptr += now_read; - } - free(buf_ptr); - } - storage_file_close(ap_name); - storage_file_free(ap_name); - } else { - char* app_default = "Evil Portal"; - app->ap_name = (uint8_t*)app_default; - } - evil_portal_close_storage(); -} - -char* sequential_file_resolve_path( - Storage* storage, - const char* dir, - const char* prefix, - const char* extension) { - if(storage == NULL || dir == NULL || prefix == NULL || extension == NULL) { - return NULL; - } - - char file_path[256]; - int file_index = 0; - - do { - if(snprintf( - file_path, sizeof(file_path), "%s/%s_%d.%s", dir, prefix, file_index, extension) < - 0) { - return NULL; - } - file_index++; - } while(storage_file_exists(storage, file_path)); - - return strdup(file_path); -} - -void write_logs(FuriString* portal_logs) { - Storage* storage = evil_portal_open_storage(); - - if(!storage_file_exists(storage, EVIL_PORTAL_LOG_SAVE_PATH)) { - storage_simply_mkdir(storage, EVIL_PORTAL_LOG_SAVE_PATH); - } - - char* seq_file_path = - sequential_file_resolve_path(storage, EVIL_PORTAL_LOG_SAVE_PATH, "log", "txt"); - - File* file = storage_file_alloc(storage); - - if(storage_file_open(file, seq_file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { - storage_file_write( - file, furi_string_get_cstr(portal_logs), furi_string_utf8_length(portal_logs)); - } - storage_file_close(file); - storage_file_free(file); - evil_portal_close_storage(); -} \ No newline at end of file diff --git a/applications/external/esp32cam_evilportal/helpers/evil_portal_storage.h b/applications/external/esp32cam_evilportal/helpers/evil_portal_storage.h deleted file mode 100644 index 286ecbd76..000000000 --- a/applications/external/esp32cam_evilportal/helpers/evil_portal_storage.h +++ /dev/null @@ -1,20 +0,0 @@ -#include "../evil_portal_app_i.h" -#include -#include -#include -#include -#include - -#define PORTAL_FILE_DIRECTORY_PATH EXT_PATH("apps_data/evil_portal") -#define EVIL_PORTAL_INDEX_SAVE_PATH PORTAL_FILE_DIRECTORY_PATH "/index.html" -#define EVIL_PORTAL_AP_SAVE_PATH PORTAL_FILE_DIRECTORY_PATH "/ap.config.txt" -#define EVIL_PORTAL_LOG_SAVE_PATH PORTAL_FILE_DIRECTORY_PATH "/logs" - -void evil_portal_read_index_html(void* context); -void evil_portal_read_ap_name(void* context); -void write_logs(FuriString* portal_logs); -char* sequential_file_resolve_path( - Storage* storage, - const char* dir, - const char* prefix, - const char* extension); diff --git a/applications/external/esp32cam_evilportal/icons/evil_portal_10px.png b/applications/external/esp32cam_evilportal/icons/evil_portal_10px.png deleted file mode 100644 index eed8789a405660a0fe46df99fd734b403bdf852b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 156 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4F%}28J29*~C-V}>3GsAs4ABUl z+JBJufB^^d+rRD0jJP;FF0J}G;dr5$iSgkomB+W2350#{V4RS^yxf$@zBlSi2E)xl zx8-leVi~vpWxBs8bgAs**2O<&M14qJbbQX$9}GQlf$yJm6xaaGXYh3Ob6Mw<(8Rz1 E0F6&L5dZ)H diff --git a/applications/external/esp32cam_evilportal/scenes/evil_portal_scene.c b/applications/external/esp32cam_evilportal/scenes/evil_portal_scene.c deleted file mode 100644 index f6f06d8cc..000000000 --- a/applications/external/esp32cam_evilportal/scenes/evil_portal_scene.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "evil_portal_scene.h" - -// Generate scene on_enter handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, -void (*const evil_portal_scene_on_enter_handlers[])(void*) = { -#include "evil_portal_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_event handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, -bool (*const evil_portal_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { -#include "evil_portal_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_exit handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, -void (*const evil_portal_scene_on_exit_handlers[])(void* context) = { -#include "evil_portal_scene_config.h" -}; -#undef ADD_SCENE - -// Initialize scene handlers configuration structure -const SceneManagerHandlers evil_portal_scene_handlers = { - .on_enter_handlers = evil_portal_scene_on_enter_handlers, - .on_event_handlers = evil_portal_scene_on_event_handlers, - .on_exit_handlers = evil_portal_scene_on_exit_handlers, - .scene_num = Evil_PortalSceneNum, -}; diff --git a/applications/external/esp32cam_evilportal/scenes/evil_portal_scene.h b/applications/external/esp32cam_evilportal/scenes/evil_portal_scene.h deleted file mode 100644 index 8468f9157..000000000 --- a/applications/external/esp32cam_evilportal/scenes/evil_portal_scene.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -// Generate scene id and total number -#define ADD_SCENE(prefix, name, id) Evil_PortalScene##id, -typedef enum { -#include "evil_portal_scene_config.h" - Evil_PortalSceneNum, -} Evil_PortalScene; -#undef ADD_SCENE - -extern const SceneManagerHandlers evil_portal_scene_handlers; - -// Generate scene on_enter handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); -#include "evil_portal_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_event handlers declaration -#define ADD_SCENE(prefix, name, id) \ - bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); -#include "evil_portal_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_exit handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); -#include "evil_portal_scene_config.h" -#undef ADD_SCENE diff --git a/applications/external/esp32cam_evilportal/scenes/evil_portal_scene_config.h b/applications/external/esp32cam_evilportal/scenes/evil_portal_scene_config.h deleted file mode 100644 index 94b09ae46..000000000 --- a/applications/external/esp32cam_evilportal/scenes/evil_portal_scene_config.h +++ /dev/null @@ -1,2 +0,0 @@ -ADD_SCENE(evil_portal, start, Start) -ADD_SCENE(evil_portal, console_output, ConsoleOutput) diff --git a/applications/external/esp32cam_evilportal/scenes/evil_portal_scene_console_output.c b/applications/external/esp32cam_evilportal/scenes/evil_portal_scene_console_output.c deleted file mode 100644 index 0447e2727..000000000 --- a/applications/external/esp32cam_evilportal/scenes/evil_portal_scene_console_output.c +++ /dev/null @@ -1,147 +0,0 @@ -#include "../evil_portal_app_i.h" -#include "../helpers/evil_portal_storage.h" - -void evil_portal_console_output_handle_rx_data_cb(uint8_t* buf, size_t len, void* context) { - furi_assert(context); - Evil_PortalApp* app = context; - - // If text box store gets too big, then truncate it - app->text_box_store_strlen += len; - if(app->text_box_store_strlen >= EVIL_PORTAL_TEXT_BOX_STORE_SIZE - 1) { - furi_string_right(app->text_box_store, app->text_box_store_strlen / 2); - app->text_box_store_strlen = furi_string_size(app->text_box_store) + len; - } - - // Null-terminate buf and append to text box store - buf[len] = '\0'; - furi_string_cat_printf(app->text_box_store, "%s", buf); - - view_dispatcher_send_custom_event(app->view_dispatcher, Evil_PortalEventRefreshConsoleOutput); -} - -void evil_portal_scene_console_output_on_enter(void* context) { - Evil_PortalApp* app = context; - - TextBox* text_box = app->text_box; - text_box_reset(app->text_box); - text_box_set_font(text_box, TextBoxFontText); - if(app->focus_console_start) { - text_box_set_focus(text_box, TextBoxFocusStart); - } else { - text_box_set_focus(text_box, TextBoxFocusEnd); - } - - if(app->is_command) { - furi_string_reset(app->text_box_store); - app->text_box_store_strlen = 0; - app->sent_reset = false; - - if(0 == strncmp("help", app->selected_tx_string, strlen("help"))) { - const char* help_msg = "BLUE = Waiting\nGREEN = Good\nRED = Bad\n\nThis project is a " - "WIP.\ngithub.com/bigbrodude6119/flipper-zero-evil-portal\n\n" - "Version 0.0.2\n\n"; - furi_string_cat_str(app->text_box_store, help_msg); - app->text_box_store_strlen += strlen(help_msg); - if(app->show_stopscan_tip) { - const char* msg = "Press BACK to return\n"; - furi_string_cat_str(app->text_box_store, msg); - app->text_box_store_strlen += strlen(msg); - } - } - - if(0 == strncmp("savelogs", app->selected_tx_string, strlen("savelogs"))) { - const char* help_msg = "Logs saved.\n\n"; - furi_string_cat_str(app->text_box_store, help_msg); - app->text_box_store_strlen += strlen(help_msg); - write_logs(app->portal_logs); - furi_string_reset(app->portal_logs); - if(app->show_stopscan_tip) { - const char* msg = "Press BACK to return\n"; - furi_string_cat_str(app->text_box_store, msg); - app->text_box_store_strlen += strlen(msg); - } - } - - if(0 == strncmp(SET_HTML_CMD, app->selected_tx_string, strlen(SET_HTML_CMD))) { - app->command_queue[0] = SET_AP_CMD; - app->has_command_queue = true; - app->command_index = 0; - if(app->show_stopscan_tip) { - const char* msg = "Starting portal\nIf no response press\nBACK to return\n"; - furi_string_cat_str(app->text_box_store, msg); - app->text_box_store_strlen += strlen(msg); - } - } - - if(0 == strncmp(RESET_CMD, app->selected_tx_string, strlen(RESET_CMD))) { - app->sent_reset = true; - if(app->show_stopscan_tip) { - const char* msg = "Reseting portal\nPress BACK to return\n\n\n\n"; - furi_string_cat_str(app->text_box_store, msg); - app->text_box_store_strlen += strlen(msg); - } - } - } - - text_box_set_text(app->text_box, furi_string_get_cstr(app->text_box_store)); - - scene_manager_set_scene_state(app->scene_manager, Evil_PortalSceneConsoleOutput, 0); - view_dispatcher_switch_to_view(app->view_dispatcher, Evil_PortalAppViewConsoleOutput); - - // Register callback to receive data - evil_portal_uart_set_handle_rx_data_cb( - app->uart, evil_portal_console_output_handle_rx_data_cb); - - if(app->is_command && app->selected_tx_string) { - if(0 == strncmp(SET_HTML_CMD, app->selected_tx_string, strlen(SET_HTML_CMD))) { - evil_portal_read_index_html(context); - - FuriString* data = furi_string_alloc(); - furi_string_cat(data, "sethtml="); - furi_string_cat(data, (char*)app->index_html); - - evil_portal_uart_tx( - (uint8_t*)(furi_string_get_cstr(data)), strlen(furi_string_get_cstr(data))); - evil_portal_uart_tx((uint8_t*)("\n"), 1); - - app->sent_html = true; - - free(data); - free(app->index_html); - - evil_portal_read_ap_name(context); - } else if(0 == strncmp(RESET_CMD, app->selected_tx_string, strlen(RESET_CMD))) { - app->sent_html = false; - app->sent_ap = false; - evil_portal_uart_tx( - (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); - evil_portal_uart_tx((uint8_t*)("\n"), 1); - } else if(1 == strncmp("help", app->selected_tx_string, strlen("help"))) { - evil_portal_uart_tx( - (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); - evil_portal_uart_tx((uint8_t*)("\n"), 1); - } - } -} - -bool evil_portal_scene_console_output_on_event(void* context, SceneManagerEvent event) { - Evil_PortalApp* app = context; - - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - text_box_set_text(app->text_box, furi_string_get_cstr(app->text_box_store)); - consumed = true; - } else if(event.type == SceneManagerEventTypeTick) { - consumed = true; - } - - return consumed; -} - -void evil_portal_scene_console_output_on_exit(void* context) { - Evil_PortalApp* app = context; - - // Unregister rx callback - evil_portal_uart_set_handle_rx_data_cb(app->uart, NULL); -} diff --git a/applications/external/esp32cam_evilportal/scenes/evil_portal_scene_start.c b/applications/external/esp32cam_evilportal/scenes/evil_portal_scene_start.c deleted file mode 100644 index 7f7200fcb..000000000 --- a/applications/external/esp32cam_evilportal/scenes/evil_portal_scene_start.c +++ /dev/null @@ -1,130 +0,0 @@ -#include "../evil_portal_app_i.h" - -// For each command, define whether additional arguments are needed -// (enabling text input to fill them out), and whether the console -// text box should focus at the start of the output or the end -typedef enum { NO_ARGS = 0, INPUT_ARGS, TOGGLE_ARGS } InputArgs; - -typedef enum { FOCUS_CONSOLE_END = 0, FOCUS_CONSOLE_START, FOCUS_CONSOLE_TOGGLE } FocusConsole; - -#define SHOW_STOPSCAN_TIP (true) -#define NO_TIP (false) - -#define MAX_OPTIONS (9) -typedef struct { - const char* item_string; - const char* options_menu[MAX_OPTIONS]; - int num_options_menu; - const char* actual_commands[MAX_OPTIONS]; - InputArgs needs_keyboard; - FocusConsole focus_console; - bool show_stopscan_tip; -} Evil_PortalItem; - -// NUM_MENU_ITEMS defined in evil_portal_app_i.h - if you add an entry here, -// increment it! -const Evil_PortalItem items[NUM_MENU_ITEMS] = { - // send command - {"Start portal", {""}, 1, {SET_HTML_CMD}, NO_ARGS, FOCUS_CONSOLE_END, SHOW_STOPSCAN_TIP}, - - // stop portal - {"Stop portal", {""}, 1, {RESET_CMD}, NO_ARGS, FOCUS_CONSOLE_START, SHOW_STOPSCAN_TIP}, - - // console - {"Save logs", {""}, 1, {"savelogs"}, NO_ARGS, FOCUS_CONSOLE_START, SHOW_STOPSCAN_TIP}, - - // help - {"Help", {""}, 1, {"help"}, NO_ARGS, FOCUS_CONSOLE_START, SHOW_STOPSCAN_TIP}, -}; - -static void evil_portal_scene_start_var_list_enter_callback(void* context, uint32_t index) { - furi_assert(context); - Evil_PortalApp* app = context; - - furi_assert(index < NUM_MENU_ITEMS); - const Evil_PortalItem* item = &items[index]; - - const int selected_option_index = app->selected_option_index[index]; - furi_assert(selected_option_index < item->num_options_menu); - app->selected_tx_string = item->actual_commands[selected_option_index]; - app->is_command = true; - app->is_custom_tx_string = false; - app->selected_menu_index = index; - app->focus_console_start = (item->focus_console == FOCUS_CONSOLE_TOGGLE) ? - (selected_option_index == 0) : - item->focus_console; - app->show_stopscan_tip = item->show_stopscan_tip; - - view_dispatcher_send_custom_event(app->view_dispatcher, Evil_PortalEventStartConsole); -} - -static void evil_portal_scene_start_var_list_change_callback(VariableItem* item) { - furi_assert(item); - - Evil_PortalApp* app = variable_item_get_context(item); - furi_assert(app); - - const Evil_PortalItem* menu_item = &items[app->selected_menu_index]; - uint8_t item_index = variable_item_get_current_value_index(item); - furi_assert(item_index < menu_item->num_options_menu); - variable_item_set_current_value_text(item, menu_item->options_menu[item_index]); - app->selected_option_index[app->selected_menu_index] = item_index; -} - -void evil_portal_scene_start_on_enter(void* context) { - Evil_PortalApp* app = context; - VariableItemList* var_item_list = app->var_item_list; - - variable_item_list_set_enter_callback( - var_item_list, evil_portal_scene_start_var_list_enter_callback, app); - - VariableItem* item; - for(int i = 0; i < NUM_MENU_ITEMS; ++i) { - item = variable_item_list_add( - var_item_list, - items[i].item_string, - items[i].num_options_menu, - evil_portal_scene_start_var_list_change_callback, - app); - variable_item_set_current_value_index(item, app->selected_option_index[i]); - variable_item_set_current_value_text( - item, items[i].options_menu[app->selected_option_index[i]]); - } - - variable_item_list_set_selected_item( - var_item_list, scene_manager_get_scene_state(app->scene_manager, Evil_PortalSceneStart)); - - view_dispatcher_switch_to_view(app->view_dispatcher, Evil_PortalAppViewVarItemList); -} - -bool evil_portal_scene_start_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - Evil_PortalApp* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == Evil_PortalEventStartPortal) { - scene_manager_set_scene_state( - app->scene_manager, Evil_PortalSceneStart, app->selected_menu_index); - scene_manager_next_scene(app->scene_manager, Evil_PortalAppViewStartPortal); - } else if(event.event == Evil_PortalEventStartKeyboard) { - scene_manager_set_scene_state( - app->scene_manager, Evil_PortalSceneStart, app->selected_menu_index); - } else if(event.event == Evil_PortalEventStartConsole) { - scene_manager_set_scene_state( - app->scene_manager, Evil_PortalSceneStart, app->selected_menu_index); - scene_manager_next_scene(app->scene_manager, Evil_PortalAppViewConsoleOutput); - } - consumed = true; - } else if(event.type == SceneManagerEventTypeTick) { - app->selected_menu_index = variable_item_list_get_selected_item_index(app->var_item_list); - consumed = true; - } - - return consumed; -} - -void evil_portal_scene_start_on_exit(void* context) { - Evil_PortalApp* app = context; - variable_item_list_reset(app->var_item_list); -} diff --git a/applications/external/esp32cam_marauder_companion/LICENSE b/applications/external/esp32cam_marauder_companion/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/applications/external/esp32cam_marauder_companion/application.fam b/applications/external/esp32cam_marauder_companion/application.fam index 891c0859d..87e30c040 100644 --- a/applications/external/esp32cam_marauder_companion/application.fam +++ b/applications/external/esp32cam_marauder_companion/application.fam @@ -1,12 +1,13 @@ App( appid="mayhem_marauder", name="[MAYHEM] Marauder", + fap_version=(6, 0), apptype=FlipperAppType.EXTERNAL, entry_point="wifi_marauder_app", - cdefines=["APP_WIFI_MARAUDER"], requires=["gui"], stack_size=4 * 1024, fap_icon="wifi_10px.png", fap_category="GPIO", + fap_icon_assets="assets", fap_description="ESP32-CAM version of Marauder. Includes all functionality from the original plus some options to trigger the camera and flashlight. [Unplug the USB cable to test with Mayhem]", ) diff --git a/applications/external/esp32cam_marauder_companion/assets/Text_10x10.png b/applications/external/esp32cam_marauder_companion/assets/Text_10x10.png new file mode 100644 index 0000000000000000000000000000000000000000..8e8a6183dd50535729dc9c9b4f220a12dd4c600f GIT binary patch literal 158 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2VGmzZ%#=aj&u?6^qxB}__|Nk$&IsYz@rRC}3 z7*a7OIiZ2U&CSi=;0cBn1vTatM&Z;3u7g(^G9`qQn09G2aWeNXaKC0S=Q~tg57Z@F z;u=vBoS#-wo>-L1;E+?AmspUPnOCA;ke9BToS%}K{MA`f4ycg9)78&qol`;+00Iau A9smFU literal 0 HcmV?d00001 diff --git a/applications/external/esp32cam_marauder_companion/file/sequential_file.c b/applications/external/esp32cam_marauder_companion/file/sequential_file.c new file mode 100644 index 000000000..d780deb12 --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/file/sequential_file.c @@ -0,0 +1,46 @@ +#include "sequential_file.h" + +char* sequential_file_resolve_path( + Storage* storage, + const char* dir, + const char* prefix, + const char* extension) { + if(storage == NULL || dir == NULL || prefix == NULL || extension == NULL) { + return NULL; + } + + char file_path[256]; + int file_index = 0; + + do { + if(snprintf( + file_path, sizeof(file_path), "%s/%s_%d.%s", dir, prefix, file_index, extension) < + 0) { + return NULL; + } + file_index++; + } while(storage_file_exists(storage, file_path)); + + return strdup(file_path); +} + +bool sequential_file_open( + Storage* storage, + File* file, + const char* dir, + const char* prefix, + const char* extension) { + if(storage == NULL || file == NULL || dir == NULL || prefix == NULL || extension == NULL) { + return false; + } + + char* file_path = sequential_file_resolve_path(storage, dir, prefix, extension); + if(file_path == NULL) { + return false; + } + + bool success = storage_file_open(file, file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS); + free(file_path); + + return success; +} \ No newline at end of file diff --git a/applications/external/esp32cam_marauder_companion/file/sequential_file.h b/applications/external/esp32cam_marauder_companion/file/sequential_file.h new file mode 100644 index 000000000..4fd4794c2 --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/file/sequential_file.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +char* sequential_file_resolve_path( + Storage* storage, + const char* dir, + const char* prefix, + const char* extension); +bool sequential_file_open( + Storage* storage, + File* file, + const char* dir, + const char* prefix, + const char* extension); \ No newline at end of file diff --git a/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_config.h b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_config.h index 715897d17..d223af79a 100644 --- a/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_config.h +++ b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_config.h @@ -3,3 +3,13 @@ ADD_SCENE(wifi_marauder, console_output, ConsoleOutput) ADD_SCENE(wifi_marauder, text_input, TextInput) ADD_SCENE(wifi_marauder, settings_init, SettingsInit) ADD_SCENE(wifi_marauder, log_viewer, LogViewer) +ADD_SCENE(wifi_marauder, user_input, UserInput) +ADD_SCENE(wifi_marauder, script_select, ScriptSelect) +ADD_SCENE(wifi_marauder, script_options, ScriptOptions) +ADD_SCENE(wifi_marauder, script_edit, ScriptEdit) +ADD_SCENE(wifi_marauder, script_settings, ScriptSettings) +ADD_SCENE(wifi_marauder, script_confirm_delete, ScriptConfirmDelete) +ADD_SCENE(wifi_marauder, script_stage_edit, ScriptStageEdit) +ADD_SCENE(wifi_marauder, script_stage_add, ScriptStageAdd) +ADD_SCENE(wifi_marauder, script_stage_edit_list, ScriptStageEditList) +ADD_SCENE(wifi_marauder, sniffpmkid_options, SniffPmkidOptions) diff --git a/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_console_output.c b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_console_output.c index fd2d48035..05d94fe80 100644 --- a/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_console_output.c +++ b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_console_output.c @@ -1,5 +1,34 @@ #include "../wifi_marauder_app_i.h" +char* _wifi_marauder_get_prefix_from_cmd(const char* command) { + int end = strcspn(command, " "); + char* prefix = (char*)malloc(sizeof(char) * (end + 1)); + strncpy(prefix, command, end); + prefix[end] = '\0'; + return prefix; +} + +bool _wifi_marauder_is_save_pcaps_enabled(WifiMarauderApp* app) { + if(!app->ok_to_save_pcaps) { + return false; + } + // If it is a script that contains a sniff function + if(app->script != NULL) { + return wifi_marauder_script_has_stage(app->script, WifiMarauderScriptStageTypeSniffRaw) || + wifi_marauder_script_has_stage( + app->script, WifiMarauderScriptStageTypeSniffBeacon) || + wifi_marauder_script_has_stage( + app->script, WifiMarauderScriptStageTypeSniffDeauth) || + wifi_marauder_script_has_stage(app->script, WifiMarauderScriptStageTypeSniffEsp) || + wifi_marauder_script_has_stage( + app->script, WifiMarauderScriptStageTypeSniffPmkid) || + wifi_marauder_script_has_stage(app->script, WifiMarauderScriptStageTypeSniffPwn); + } + // If it is a sniff function + return app->is_command && app->selected_tx_string && + strncmp("sniff", app->selected_tx_string, strlen("sniff")) == 0; +} + void wifi_marauder_console_output_handle_rx_data_cb(uint8_t* buf, size_t len, void* context) { furi_assert(context); WifiMarauderApp* app = context; @@ -34,23 +63,29 @@ void wifi_marauder_console_output_handle_rx_packets_cb(uint8_t* buf, size_t len, void wifi_marauder_scene_console_output_on_enter(void* context) { WifiMarauderApp* app = context; + // Reset text box and set font TextBox* text_box = app->text_box; - text_box_reset(app->text_box); + text_box_reset(text_box); text_box_set_font(text_box, TextBoxFontText); + + // Set focus on start or end if(app->focus_console_start) { text_box_set_focus(text_box, TextBoxFocusStart); } else { text_box_set_focus(text_box, TextBoxFocusEnd); } + + // Set command-related messages if(app->is_command) { furi_string_reset(app->text_box_store); app->text_box_store_strlen = 0; + // Help message if(0 == strncmp("help", app->selected_tx_string, strlen("help"))) { const char* help_msg = "Marauder companion " WIFI_MARAUDER_APP_VERSION "\n"; furi_string_cat_str(app->text_box_store, help_msg); app->text_box_store_strlen += strlen(help_msg); } - + // Stopscan message if(app->show_stopscan_tip) { const char* help_msg = "Press BACK to send stopscan\n"; furi_string_cat_str(app->text_box_store, help_msg); @@ -58,13 +93,14 @@ void wifi_marauder_scene_console_output_on_enter(void* context) { } } - // Set starting text - for "View Log from end", this will just be what was already in the text box store + // Set starting text text_box_set_text(app->text_box, furi_string_get_cstr(app->text_box_store)); + // Set scene state and switch view scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneConsoleOutput, 0); view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewConsoleOutput); - // Register callback to receive data + // Register callbacks to receive data wifi_marauder_uart_set_handle_rx_data_cb( app->uart, wifi_marauder_console_output_handle_rx_data_cb); // setup callback for general log rx thread @@ -73,25 +109,53 @@ void wifi_marauder_scene_console_output_on_enter(void* context) { wifi_marauder_console_output_handle_rx_packets_cb); // setup callback for packets rx thread // Get ready to send command - if(app->is_command && app->selected_tx_string) { + if((app->is_command && app->selected_tx_string) || app->script) { + const char* prefix = + strlen(app->selected_tx_string) > 0 ? + _wifi_marauder_get_prefix_from_cmd(app->selected_tx_string) : // Function name + app->script->name; // Script name + // Create files *before* sending command // (it takes time to iterate through the directory) if(app->ok_to_save_logs) { - app->is_writing_log = true; - wifi_marauder_create_log_file(app); + strcpy( + app->log_file_path, + sequential_file_resolve_path( + app->storage, MARAUDER_APP_FOLDER_LOGS, prefix, "log")); + if(app->log_file_path != NULL) { + if(storage_file_open( + app->log_file, app->log_file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + app->is_writing_log = true; + } else { + dialog_message_show_storage_error(app->dialogs, "Cannot open log file"); + } + } else { + dialog_message_show_storage_error(app->dialogs, "Cannot resolve log path"); + } } - // If it is a sniff function, open the pcap file for recording - if(app->ok_to_save_pcaps && - strncmp("sniff", app->selected_tx_string, strlen("sniff")) == 0) { - app->is_writing_pcap = true; - wifi_marauder_create_pcap_file(app); + // If it is a sniff function or script, open the pcap file for recording + if(_wifi_marauder_is_save_pcaps_enabled(app)) { + if(sequential_file_open( + app->storage, app->capture_file, MARAUDER_APP_FOLDER_PCAPS, prefix, "pcap")) { + app->is_writing_pcap = true; + } else { + dialog_message_show_storage_error(app->dialogs, "Cannot open pcap file"); + } } // Send command with newline '\n' - wifi_marauder_uart_tx( - (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); - wifi_marauder_uart_tx((uint8_t*)("\n"), 1); + if(app->selected_tx_string) { + wifi_marauder_uart_tx( + (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); + wifi_marauder_uart_tx((uint8_t*)("\n"), 1); + } + + // Run the script if the file with the script has been opened + if(app->script != NULL) { + app->script_worker = wifi_marauder_script_worker_alloc(); + wifi_marauder_script_worker_start(app->script_worker, app->script); + } } } @@ -113,14 +177,18 @@ bool wifi_marauder_scene_console_output_on_event(void* context, SceneManagerEven void wifi_marauder_scene_console_output_on_exit(void* context) { WifiMarauderApp* app = context; + // Automatically stop the scan when exiting view + if(app->is_command) { + wifi_marauder_uart_tx((uint8_t*)("stopscan\n"), strlen("stopscan\n")); + furi_delay_ms(50); + } + // Unregister rx callback wifi_marauder_uart_set_handle_rx_data_cb(app->uart, NULL); wifi_marauder_uart_set_handle_rx_data_cb(app->lp_uart, NULL); - // Automatically stop the scan when exiting view - if(app->is_command) { - wifi_marauder_uart_tx((uint8_t*)("stopscan\n"), strlen("stopscan\n")); - } + wifi_marauder_script_worker_free(app->script_worker); + app->script_worker = NULL; app->is_writing_pcap = false; if(app->capture_file && storage_file_is_open(app->capture_file)) { @@ -131,4 +199,4 @@ void wifi_marauder_scene_console_output_on_exit(void* context) { if(app->log_file && storage_file_is_open(app->log_file)) { storage_file_close(app->log_file); } -} \ No newline at end of file +} diff --git a/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_log_viewer.c b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_log_viewer.c index f4e84ccc8..6edb4a49d 100644 --- a/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_log_viewer.c +++ b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_log_viewer.c @@ -148,8 +148,10 @@ bool wifi_marauder_scene_log_viewer_on_event(void* context, SceneManagerEvent ev // Browse FuriString* predefined_filepath = furi_string_alloc_set_str(MARAUDER_APP_FOLDER_LOGS); FuriString* selected_filepath = furi_string_alloc(); + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, ".log", &I_Text_10x10); if(dialog_file_browser_show( - app->dialogs, selected_filepath, predefined_filepath, NULL)) { + app->dialogs, selected_filepath, predefined_filepath, &browser_options)) { strncpy( app->log_file_path, furi_string_get_cstr(selected_filepath), diff --git a/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_confirm_delete.c b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_confirm_delete.c new file mode 100644 index 000000000..8e436fe2b --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_confirm_delete.c @@ -0,0 +1,83 @@ +#include "../wifi_marauder_app_i.h" + +void wifi_marauder_scene_script_confirm_delete_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + WifiMarauderApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void wifi_marauder_scene_script_confirm_delete_on_enter(void* context) { + WifiMarauderApp* app = context; + + widget_add_button_element( + app->widget, + GuiButtonTypeLeft, + "No", + wifi_marauder_scene_script_confirm_delete_widget_callback, + app); + widget_add_button_element( + app->widget, + GuiButtonTypeRight, + "Yes", + wifi_marauder_scene_script_confirm_delete_widget_callback, + app); + + widget_add_string_element( + app->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Are you sure?"); + widget_add_text_box_element( + app->widget, + 0, + 12, + 128, + 38, + AlignCenter, + AlignCenter, + "The script will be\npermanently deleted", + false); + + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewWidget); +} + +bool wifi_marauder_scene_script_confirm_delete_on_event(void* context, SceneManagerEvent event) { + WifiMarauderApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + // get which button press: "Yes" or "No" + if(event.event == GuiButtonTypeRight) { + // Yes + if(app->script != NULL) { + char script_path[256]; + snprintf( + script_path, + sizeof(script_path), + "%s/%s.json", + MARAUDER_APP_FOLDER_SCRIPTS, + app->script->name); + storage_simply_remove(app->storage, script_path); + wifi_marauder_script_free(app->script); + app->script = NULL; + + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_text(message, "Deleted!", 88, 32, AlignCenter, AlignCenter); + dialog_message_set_icon(message, &I_DolphinCommon_56x48, 5, 6); + dialog_message_set_buttons(message, NULL, "Ok", NULL); + dialog_message_show(app->dialogs, message); + dialog_message_free(message); + } + } + scene_manager_previous_scene(app->scene_manager); + consumed = true; + } + + return consumed; +} + +void wifi_marauder_scene_script_confirm_delete_on_exit(void* context) { + WifiMarauderApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_edit.c b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_edit.c new file mode 100644 index 000000000..697daba4f --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_edit.c @@ -0,0 +1,125 @@ +#include "../wifi_marauder_app_i.h" + +static void wifi_marauder_scene_script_edit_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + WifiMarauderScriptStage* current_stage = app->script->first_stage; + uint32_t stage_index = 0; + + while(current_stage != NULL && stage_index < index) { + current_stage = current_stage->next_stage; + stage_index++; + } + app->script_edit_selected_stage = current_stage; + + if(app->script_edit_selected_stage != NULL) { + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageEdit); + } +} + +static void wifi_marauder_scene_script_edit_add_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageAdd); +} + +void wifi_marauder_scene_script_edit_on_enter(void* context) { + WifiMarauderApp* app = context; + Submenu* submenu = app->submenu; + WifiMarauderScript* script = app->script; + submenu_set_header(submenu, script->name); + + WifiMarauderScriptStage* current_stage = script->first_stage; + int stage_index = 0; + while(current_stage != NULL) { + switch(current_stage->type) { + case WifiMarauderScriptStageTypeScan: + submenu_add_item( + submenu, "Scan", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeSelect: + submenu_add_item( + submenu, "Select", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeDeauth: + submenu_add_item( + submenu, "Deauth", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeProbe: + submenu_add_item( + submenu, "Probe", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeSniffRaw: + submenu_add_item( + submenu, "Sniff raw", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeSniffBeacon: + submenu_add_item( + submenu, + "Sniff beacon", + stage_index, + wifi_marauder_scene_script_edit_callback, + app); + break; + case WifiMarauderScriptStageTypeSniffDeauth: + submenu_add_item( + submenu, + "Sniff deauth", + stage_index, + wifi_marauder_scene_script_edit_callback, + app); + break; + case WifiMarauderScriptStageTypeSniffEsp: + submenu_add_item( + submenu, "Sniff esp", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeSniffPmkid: + submenu_add_item( + submenu, "Sniff PMKID", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeSniffPwn: + submenu_add_item( + submenu, "Sniff pwn", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeBeaconList: + submenu_add_item( + submenu, "Beacon list", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeBeaconAp: + submenu_add_item( + submenu, "Beacon AP", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeExec: + submenu_add_item( + submenu, + "Custom command", + stage_index, + wifi_marauder_scene_script_edit_callback, + app); + break; + case WifiMarauderScriptStageTypeDelay: + submenu_add_item( + submenu, "Delay", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + } + current_stage = current_stage->next_stage; + stage_index++; + } + + submenu_add_item( + submenu, "[+] ADD STAGE", stage_index++, wifi_marauder_scene_script_edit_add_callback, app); + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); +} + +bool wifi_marauder_scene_script_edit_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_edit_on_exit(void* context) { + WifiMarauderApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_edit_list.c b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_edit_list.c new file mode 100644 index 000000000..7a3284caf --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_edit_list.c @@ -0,0 +1,188 @@ +#include "../wifi_marauder_app_i.h" + +static void + wifi_marauder_scene_script_stage_edit_list_add_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + + // Creates new item + WifiMarauderScriptStageListItem* new_item = + (WifiMarauderScriptStageListItem*)malloc(sizeof(WifiMarauderScriptStageListItem)); + new_item->value = malloc(64); + new_item->next_item = NULL; + + if(app->script_stage_edit_first_item == NULL) { + app->script_stage_edit_first_item = new_item; + } else { + WifiMarauderScriptStageListItem* last_item = app->script_stage_edit_first_item; + while(last_item->next_item != NULL) { + last_item = last_item->next_item; + } + last_item->next_item = new_item; + } + + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEditList, index); + app->user_input_type = WifiMarauderUserInputTypeString; + app->user_input_string_reference = &new_item->value; + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput); +} + +static void wifi_marauder_scene_script_stage_edit_list_deallocate_items(WifiMarauderApp* app) { + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + while(current_item != NULL) { + WifiMarauderScriptStageListItem* next_item = current_item->next_item; + free(current_item->value); + free(current_item); + current_item = next_item; + } + app->script_stage_edit_first_item = NULL; +} + +static void wifi_marauder_scene_script_stage_edit_list_save_strings(WifiMarauderApp* app) { + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + int array_size = 0; + + // Calculates the required array size + while(current_item != NULL) { + array_size++; + current_item = current_item->next_item; + } + + // Reallocate the array of strings if necessary + if(*app->script_stage_edit_string_count_reference < array_size) { + *app->script_stage_edit_strings_reference = + realloc(*app->script_stage_edit_strings_reference, array_size * sizeof(char*)); + } + + // Fills the array of strings + current_item = app->script_stage_edit_first_item; + int i = 0; + while(current_item != NULL) { + char* current_str = malloc(strlen(current_item->value) + 1); + strncpy(current_str, current_item->value, strlen(current_item->value) + 1); + (*app->script_stage_edit_strings_reference)[i] = current_str; + current_item = current_item->next_item; + i++; + } + + *app->script_stage_edit_string_count_reference = array_size; +} + +static void wifi_marauder_scene_script_stage_edit_list_save_numbers(WifiMarauderApp* app) { + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + int array_size = 0; + + // Calculates the required array size + while(current_item != NULL) { + array_size++; + current_item = current_item->next_item; + } + + // Reallocate the array of integers if necessary + if(*app->script_stage_edit_number_count_reference < array_size) { + *app->script_stage_edit_numbers_reference = + realloc(*app->script_stage_edit_numbers_reference, array_size * sizeof(int)); + } + + // Fills the array of integers + current_item = app->script_stage_edit_first_item; + int i = 0; + while(current_item != NULL) { + (*app->script_stage_edit_numbers_reference)[i] = atoi(current_item->value); + current_item = current_item->next_item; + i++; + } + + *app->script_stage_edit_number_count_reference = array_size; +} + +static void + wifi_marauder_scene_script_stage_edit_list_save_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + if(app->script_stage_edit_strings_reference != NULL && + app->script_stage_edit_string_count_reference != NULL) { + wifi_marauder_scene_script_stage_edit_list_save_strings(app); + } + + if(app->script_stage_edit_numbers_reference != NULL && + app->script_stage_edit_number_count_reference != NULL) { + wifi_marauder_scene_script_stage_edit_list_save_numbers(app); + } + + wifi_marauder_scene_script_stage_edit_list_deallocate_items(app); + scene_manager_previous_scene(app->scene_manager); +} + +static void + wifi_marauder_scene_script_stage_edit_list_clear_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + wifi_marauder_scene_script_stage_edit_list_deallocate_items(app); + + submenu_reset(app->submenu); + submenu_add_item( + app->submenu, + "[+] ADD ITEM", + 99, + wifi_marauder_scene_script_stage_edit_list_add_callback, + app); + submenu_add_item( + app->submenu, + "[*] SAVE ITEMS", + 99, + wifi_marauder_scene_script_stage_edit_list_save_callback, + app); + submenu_add_item( + app->submenu, + "[-] CLEAR LIST", + 99, + wifi_marauder_scene_script_stage_edit_list_clear_callback, + app); +} + +void wifi_marauder_scene_script_stage_edit_list_on_enter(void* context) { + WifiMarauderApp* app = context; + int item_index = 0; + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + + while(current_item != NULL) { + submenu_add_item(app->submenu, current_item->value, item_index++, NULL, app); + current_item = current_item->next_item; + } + submenu_add_item( + app->submenu, + "[+] ADD ITEM", + 99, + wifi_marauder_scene_script_stage_edit_list_add_callback, + app); + submenu_add_item( + app->submenu, + "[*] SAVE ITEMS", + 99, + wifi_marauder_scene_script_stage_edit_list_save_callback, + app); + submenu_add_item( + app->submenu, + "[-] CLEAR LIST", + 99, + wifi_marauder_scene_script_stage_edit_list_clear_callback, + app); + + submenu_set_selected_item( + app->submenu, + scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEditList)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); +} + +bool wifi_marauder_scene_script_stage_edit_list_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_stage_edit_list_on_exit(void* context) { + WifiMarauderApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_options.c b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_options.c new file mode 100644 index 000000000..35b374f61 --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_options.c @@ -0,0 +1,111 @@ +#include "../wifi_marauder_app_i.h" + +enum SubmenuIndex { + SubmenuIndexRun, + SubmenuIndexSettings, + SubmenuIndexEditStages, + SubmenuIndexSave, + SubmenuIndexDelete +}; + +void wifi_marauder_scene_script_options_save_script(WifiMarauderApp* app) { + char script_path[256]; + snprintf( + script_path, + sizeof(script_path), + "%s/%s.json", + MARAUDER_APP_FOLDER_SCRIPTS, + app->script->name); + wifi_marauder_script_save_json(app->storage, script_path, app->script); + + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_text(message, "Saved!", 88, 32, AlignCenter, AlignCenter); + dialog_message_set_icon(message, &I_DolphinCommon_56x48, 5, 6); + dialog_message_set_buttons(message, NULL, "Ok", NULL); + dialog_message_show(app->dialogs, message); + dialog_message_free(message); +} + +static void wifi_marauder_scene_script_options_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + + switch(index) { + case SubmenuIndexRun: + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneConsoleOutput); + break; + case SubmenuIndexSettings: + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptSettings); + break; + case SubmenuIndexEditStages: + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptEdit); + break; + case SubmenuIndexSave: + wifi_marauder_scene_script_options_save_script(app); + break; + case SubmenuIndexDelete: + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptConfirmDelete); + break; + } +} + +void wifi_marauder_scene_script_options_on_enter(void* context) { + WifiMarauderApp* app = context; + + // If returning after confirming script deletion + if(app->script == NULL) { + scene_manager_previous_scene(app->scene_manager); + return; + } + + Submenu* submenu = app->submenu; + + submenu_set_header(submenu, app->script->name); + submenu_add_item( + submenu, "[>] RUN", SubmenuIndexRun, wifi_marauder_scene_script_options_callback, app); + submenu_add_item( + submenu, + "[S] SETTINGS", + SubmenuIndexSettings, + wifi_marauder_scene_script_options_callback, + app); + submenu_add_item( + submenu, + "[+] EDIT STAGES", + SubmenuIndexEditStages, + wifi_marauder_scene_script_options_callback, + app); + submenu_add_item( + submenu, "[*] SAVE", SubmenuIndexSave, wifi_marauder_scene_script_options_callback, app); + submenu_add_item( + submenu, + "[X] DELETE", + SubmenuIndexDelete, + wifi_marauder_scene_script_options_callback, + app); + + submenu_set_selected_item( + submenu, + scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); +} + +bool wifi_marauder_scene_script_options_on_event(void* context, SceneManagerEvent event) { + WifiMarauderApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + wifi_marauder_script_free(app->script); + app->script = NULL; + } + + return consumed; +} + +void wifi_marauder_scene_script_options_on_exit(void* context) { + WifiMarauderApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_select.c b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_select.c new file mode 100644 index 000000000..bc0746858 --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_select.c @@ -0,0 +1,90 @@ +#include "../wifi_marauder_app_i.h" + +static void wifi_marauder_scene_script_select_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + + char script_path[256]; + snprintf( + script_path, + sizeof(script_path), + "%s/%s.json", + MARAUDER_APP_FOLDER_SCRIPTS, + furi_string_get_cstr(app->script_list[index])); + + app->script = wifi_marauder_script_parse_json(app->storage, script_path); + if(app->script) { + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptSelect, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptOptions); + } +} + +static void wifi_marauder_scene_script_select_add_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptSelect, index); + + app->user_input_type = WifiMarauderUserInputTypeFileName; + app->user_input_file_dir = strdup(MARAUDER_APP_FOLDER_SCRIPTS); + app->user_input_file_extension = strdup("json"); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput); +} + +void wifi_marauder_scene_script_select_on_enter(void* context) { + WifiMarauderApp* app = context; + Submenu* submenu = app->submenu; + + File* dir_scripts = storage_file_alloc(app->storage); + if(storage_dir_open(dir_scripts, MARAUDER_APP_FOLDER_SCRIPTS)) { + FileInfo file_info; + char file_path[255]; + app->script_list_count = 0; + // Goes through the files in the folder counting the ones that end with the json extension + while(storage_dir_read(dir_scripts, &file_info, file_path, 255)) { + app->script_list_count++; + } + if(app->script_list_count > 0) { + submenu_set_header(submenu, "Select a script:"); + app->script_list = malloc(app->script_list_count * sizeof(FuriString*)); + storage_dir_close(dir_scripts); + storage_dir_open(dir_scripts, MARAUDER_APP_FOLDER_SCRIPTS); + // Read the files again from the beginning, adding the scripts in the list + int script_index = 0; + while(storage_dir_read(dir_scripts, &file_info, file_path, 255)) { + app->script_list[script_index] = furi_string_alloc(); + path_extract_filename_no_ext(file_path, app->script_list[script_index]); + submenu_add_item( + submenu, + furi_string_get_cstr(app->script_list[script_index]), + script_index, + wifi_marauder_scene_script_select_callback, + app); + script_index++; + } + } else { + submenu_set_header(submenu, "No script found"); + } + submenu_add_item( + submenu, "[+] ADD SCRIPT", 99, wifi_marauder_scene_script_select_add_callback, app); + storage_dir_close(dir_scripts); + } + storage_file_free(dir_scripts); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptSelect)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); +} + +bool wifi_marauder_scene_script_select_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_select_on_exit(void* context) { + WifiMarauderApp* app = context; + submenu_reset(app->submenu); + + for(int i = 0; i < app->script_list_count; i++) { + furi_string_free(app->script_list[i]); + } + free(app->script_list); +} diff --git a/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_settings.c b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_settings.c new file mode 100644 index 000000000..b4903af05 --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_settings.c @@ -0,0 +1,87 @@ +#include "../wifi_marauder_app_i.h" + +enum ScriptSettingsOption { + ScriptSettingsOptionRepeat, + ScriptSettingsOptionSavePcap, + ScriptSettingsOptionEnableLed +}; + +const char* option_values[3] = {"No", "Yes", "Default"}; + +static void wifi_marauder_scene_script_settings_enter_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + // Accept script repeat value + if(index == ScriptSettingsOptionRepeat) { + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptSettings, index); + app->user_input_type = WifiMarauderUserInputTypeNumber; + app->user_input_number_reference = &app->script->repeat; + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput); + } +} + +static void wifi_marauder_scene_script_settings_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + uint8_t current_option = variable_item_list_get_selected_item_index(app->var_item_list); + uint8_t option_value_index = variable_item_get_current_value_index(item); + + switch(current_option) { + case ScriptSettingsOptionSavePcap: + variable_item_set_current_value_text(item, option_values[option_value_index]); + app->script->save_pcap = option_value_index; + break; + case ScriptSettingsOptionEnableLed: + variable_item_set_current_value_text(item, option_values[option_value_index]); + app->script->enable_led = option_value_index; + break; + } +} + +void wifi_marauder_scene_script_settings_on_enter(void* context) { + WifiMarauderApp* app = context; + VariableItemList* var_item_list = app->var_item_list; + variable_item_list_set_enter_callback( + app->var_item_list, wifi_marauder_scene_script_settings_enter_callback, app); + + // Script repeat option + VariableItem* repeat_item = variable_item_list_add(app->var_item_list, "Repeat", 1, NULL, app); + char repeat_str[32]; + snprintf(repeat_str, sizeof(repeat_str), "%d", app->script->repeat); + variable_item_set_current_value_text(repeat_item, repeat_str); + + // Save PCAP option + VariableItem* save_pcap_item = variable_item_list_add( + app->var_item_list, + "Save PCAP", + 3, + wifi_marauder_scene_script_settings_change_callback, + app); + variable_item_set_current_value_index(save_pcap_item, app->script->save_pcap); + variable_item_set_current_value_text(save_pcap_item, option_values[app->script->save_pcap]); + + // Enable board LED option + VariableItem* enable_led_item = variable_item_list_add( + app->var_item_list, + "Enable LED", + 3, + wifi_marauder_scene_script_settings_change_callback, + app); + variable_item_set_current_value_index(enable_led_item, app->script->enable_led); + variable_item_set_current_value_text(enable_led_item, option_values[app->script->enable_led]); + + variable_item_list_set_selected_item( + var_item_list, + scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptSettings)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewVarItemList); +} + +bool wifi_marauder_scene_script_settings_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_settings_on_exit(void* context) { + WifiMarauderApp* app = context; + variable_item_list_reset(app->var_item_list); +} diff --git a/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_stage_add.c b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_stage_add.c new file mode 100644 index 000000000..33f1a2f03 --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_stage_add.c @@ -0,0 +1,297 @@ +#include "../wifi_marauder_app_i.h" + +// Scan +static void wifi_marauder_scene_script_stage_add_scan_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageScan* stage = + (WifiMarauderScriptStageScan*)malloc(sizeof(WifiMarauderScriptStageScan)); + stage->type = WifiMarauderScriptScanTypeAp; + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SCAN; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeScan, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Select +static void wifi_marauder_scene_script_stage_add_select_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSelect* stage = + (WifiMarauderScriptStageSelect*)malloc(sizeof(WifiMarauderScriptStageSelect)); + stage->type = WifiMarauderScriptSelectTypeAp; + stage->filter = strdup("all"); + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSelect, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Deauth +static void wifi_marauder_scene_script_stage_add_deauth_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageDeauth* stage = + (WifiMarauderScriptStageDeauth*)malloc(sizeof(WifiMarauderScriptStageDeauth)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_DEAUTH; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeDeauth, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Probe +static void wifi_marauder_scene_script_stage_add_probe_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageProbe* stage = + (WifiMarauderScriptStageProbe*)malloc(sizeof(WifiMarauderScriptStageProbe)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_PROBE; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeProbe, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff RAW +static void wifi_marauder_scene_script_stage_add_sniffraw_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffRaw* stage = + (WifiMarauderScriptStageSniffRaw*)malloc(sizeof(WifiMarauderScriptStageSniffRaw)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffRaw, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff Beacon +static void + wifi_marauder_scene_script_stage_add_sniffbeacon_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffBeacon* stage = + (WifiMarauderScriptStageSniffBeacon*)malloc(sizeof(WifiMarauderScriptStageSniffBeacon)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffBeacon, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff Deauth +static void + wifi_marauder_scene_script_stage_add_sniffdeauth_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffDeauth* stage = + (WifiMarauderScriptStageSniffDeauth*)malloc(sizeof(WifiMarauderScriptStageSniffDeauth)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffDeauth, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff Esp +static void wifi_marauder_scene_script_stage_add_sniffesp_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffEsp* stage = + (WifiMarauderScriptStageSniffEsp*)malloc(sizeof(WifiMarauderScriptStageSniffEsp)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffEsp, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff PMKID +static void + wifi_marauder_scene_script_stage_add_sniffpmkid_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffPmkid* stage = + (WifiMarauderScriptStageSniffPmkid*)malloc(sizeof(WifiMarauderScriptStageSniffPmkid)); + stage->channel = 0; + stage->force_deauth = WifiMarauderScriptBooleanTrue; + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffPmkid, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff Pwn +static void wifi_marauder_scene_script_stage_add_sniffpwn_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffPwn* stage = + (WifiMarauderScriptStageSniffPwn*)malloc(sizeof(WifiMarauderScriptStageSniffPwn)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffPwn, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Beacon list +static void + wifi_marauder_scene_script_stage_add_beaconlist_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageBeaconList* stage = + (WifiMarauderScriptStageBeaconList*)malloc(sizeof(WifiMarauderScriptStageBeaconList)); + stage->ssids = NULL; + stage->ssid_count = 0; + stage->random_ssids = 0; + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeBeaconList, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Beacon AP +static void wifi_marauder_scene_script_stage_add_beaconap_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageBeaconAp* stage = + (WifiMarauderScriptStageBeaconAp*)malloc(sizeof(WifiMarauderScriptStageBeaconAp)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeBeaconAp, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Exec +static void wifi_marauder_scene_script_stage_add_exec_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageExec* stage = + (WifiMarauderScriptStageExec*)malloc(sizeof(WifiMarauderScriptStageExec)); + stage->command = NULL; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeExec, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Delay +static void wifi_marauder_scene_script_stage_add_delay_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageDelay* stage = + (WifiMarauderScriptStageDelay*)malloc(sizeof(WifiMarauderScriptStageDelay)); + stage->timeout = 0; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeDelay, stage); + scene_manager_previous_scene(app->scene_manager); +} + +void wifi_marauder_scene_script_stage_add_on_enter(void* context) { + WifiMarauderApp* app = context; + Submenu* submenu = app->submenu; + submenu_set_header(submenu, "Add stage"); + + int menu_index = 0; + submenu_add_item( + submenu, "[+] Scan", menu_index++, wifi_marauder_scene_script_stage_add_scan_callback, app); + submenu_add_item( + submenu, + "[+] Select", + menu_index++, + wifi_marauder_scene_script_stage_add_select_callback, + app); + submenu_add_item( + submenu, + "[+] Deauth", + menu_index++, + wifi_marauder_scene_script_stage_add_deauth_callback, + app); + submenu_add_item( + submenu, + "[+] Probe", + menu_index++, + wifi_marauder_scene_script_stage_add_probe_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff RAW", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffraw_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff Beacon", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffbeacon_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff Deauth", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffdeauth_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff Esp", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffesp_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff PMKID", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffpmkid_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff Pwnagotchi", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffpwn_callback, + app); + submenu_add_item( + submenu, + "[+] Beacon List", + menu_index++, + wifi_marauder_scene_script_stage_add_beaconlist_callback, + app); + submenu_add_item( + submenu, + "[+] Beacon AP", + menu_index++, + wifi_marauder_scene_script_stage_add_beaconap_callback, + app); + submenu_add_item( + submenu, + "[+] Custom command", + menu_index++, + wifi_marauder_scene_script_stage_add_exec_callback, + app); + submenu_add_item( + submenu, + "[+] Delay", + menu_index++, + wifi_marauder_scene_script_stage_add_delay_callback, + app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); +} + +bool wifi_marauder_scene_script_stage_add_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_stage_add_on_exit(void* context) { + WifiMarauderApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_stage_edit.c b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_stage_edit.c new file mode 100644 index 000000000..b8581e3e7 --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_script_stage_edit.c @@ -0,0 +1,203 @@ +#include "../wifi_marauder_app_i.h" + +void wifi_marauder_scene_script_stage_edit_create_list_strings( + WifiMarauderApp* app, + char** strings, + int string_count) { + // Deallocates the existing list + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + while(current_item != NULL) { + WifiMarauderScriptStageListItem* next_item = current_item->next_item; + free(current_item->value); + free(current_item); + current_item = next_item; + } + + // Create a new list with numbers + WifiMarauderScriptStageListItem* first_item = NULL; + WifiMarauderScriptStageListItem* previous_item = NULL; + for(int i = 0; i < string_count; i++) { + WifiMarauderScriptStageListItem* item = malloc(sizeof(WifiMarauderScriptStageListItem)); + item->value = strdup(strings[i]); + item->next_item = NULL; + + if(previous_item == NULL) { + first_item = item; + } else { + previous_item->next_item = item; + } + previous_item = item; + } + + app->script_stage_edit_first_item = first_item; +} + +void wifi_marauder_scene_script_stage_edit_create_list_numbers( + WifiMarauderApp* app, + int* numbers, + int number_count) { + // Deallocates the existing list + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + while(current_item != NULL) { + WifiMarauderScriptStageListItem* next_item = current_item->next_item; + free(current_item->value); + free(current_item); + current_item = next_item; + } + + // Create a new list with numbers + WifiMarauderScriptStageListItem* first_item = NULL; + WifiMarauderScriptStageListItem* previous_item = NULL; + for(int i = 0; i < number_count; i++) { + char number_str[32]; + snprintf(number_str, sizeof(number_str), "%d", numbers[i]); + + WifiMarauderScriptStageListItem* item = malloc(sizeof(WifiMarauderScriptStageListItem)); + item->value = strdup(number_str); + item->next_item = NULL; + + if(previous_item == NULL) { + first_item = item; + } else { + previous_item->next_item = item; + } + previous_item = item; + } + + app->script_stage_edit_first_item = first_item; +} + +static void + wifi_marauder_scene_script_stage_edit_list_enter_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + const WifiMarauderScriptMenuItem* menu_item = &app->script_stage_menu->items[index]; + + // Fixed delete item + if(index == app->script_stage_menu->num_items) { + uint32_t deleted_stage_index = + scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit); + if(deleted_stage_index > 0) { + scene_manager_set_scene_state( + app->scene_manager, WifiMarauderSceneScriptEdit, deleted_stage_index - 1); + } + WifiMarauderScriptStage* previous_stage = NULL; + WifiMarauderScriptStage* current_stage = app->script->first_stage; + uint32_t current_stage_index = 0; + + while(current_stage != NULL && current_stage_index < deleted_stage_index) { + previous_stage = current_stage; + current_stage = current_stage->next_stage; + current_stage_index++; + } + + // Delete the stage + if(current_stage != NULL) { + if(previous_stage != NULL) { + if(current_stage->next_stage != NULL) { + previous_stage->next_stage = current_stage->next_stage; + } else { + previous_stage->next_stage = NULL; + app->script->last_stage = previous_stage; + } + } else { + if(current_stage->next_stage != NULL) { + app->script->first_stage = current_stage->next_stage; + } else { + app->script->first_stage = NULL; + app->script->last_stage = NULL; + } + } + } + app->script_edit_selected_stage = NULL; + + scene_manager_previous_scene(app->scene_manager); + return; + } + + if(menu_item->select_callback == NULL) { + return; + } + if(menu_item->type == WifiMarauderScriptMenuItemTypeNumber) { + // Accepts user number input, assigning the value to the reference passed as a parameter + menu_item->select_callback(app); + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index); + app->user_input_type = WifiMarauderUserInputTypeNumber; + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput); + } else if(menu_item->type == WifiMarauderScriptMenuItemTypeString) { + // Accepts user string input, assigning the value to the reference passed as a parameter + menu_item->select_callback(app); + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index); + app->user_input_type = WifiMarauderUserInputTypeString; + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput); + } else if(menu_item->type == WifiMarauderScriptMenuItemTypeListString) { + // Accepts the strings that compose the list + menu_item->select_callback(app); + wifi_marauder_scene_script_stage_edit_create_list_strings( + app, + *app->script_stage_edit_strings_reference, + *app->script_stage_edit_string_count_reference); + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageEditList); + } else if(menu_item->type == WifiMarauderScriptMenuItemTypeListNumber) { + // Accepts the numbers that compose the list + menu_item->select_callback(app); + wifi_marauder_scene_script_stage_edit_create_list_numbers( + app, + *app->script_stage_edit_numbers_reference, + *app->script_stage_edit_number_count_reference); + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageEditList); + } +} + +void wifi_marauder_scene_script_stage_edit_on_enter(void* context) { + WifiMarauderApp* app = context; + VariableItemList* var_item_list = app->var_item_list; + + variable_item_list_set_enter_callback( + app->var_item_list, wifi_marauder_scene_script_stage_edit_list_enter_callback, app); + app->script_stage_menu = + wifi_marauder_script_stage_menu_create(app->script_edit_selected_stage->type); + + if(app->script_stage_menu->items != NULL) { + for(uint32_t i = 0; i < app->script_stage_menu->num_items; i++) { + WifiMarauderScriptMenuItem* stage_item = &app->script_stage_menu->items[i]; + + // Changes the list item to handle it in callbacks + VariableItem* list_item = variable_item_list_add( + app->var_item_list, + stage_item->name, + stage_item->num_options, + stage_item->change_callback, + app); + + variable_item_list_set_selected_item(app->var_item_list, i); + if(stage_item->setup_callback != NULL) { + stage_item->setup_callback(list_item); + } + if(stage_item->change_callback != NULL) { + stage_item->change_callback(list_item); + } + } + } + + variable_item_list_add(app->var_item_list, "[-] DELETE STAGE", 0, NULL, app); + + variable_item_list_set_selected_item( + var_item_list, + scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewVarItemList); +} + +bool wifi_marauder_scene_script_stage_edit_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_stage_edit_on_exit(void* context) { + WifiMarauderApp* app = context; + wifi_marauder_script_stage_menu_free(app->script_stage_menu); + app->script_stage_menu = NULL; + variable_item_list_reset(app->var_item_list); +} diff --git a/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_sniffpmkid_options.c b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_sniffpmkid_options.c new file mode 100644 index 000000000..02869030e --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_sniffpmkid_options.c @@ -0,0 +1,117 @@ +#include "../wifi_marauder_app_i.h" + +enum SubmenuIndex { + SubmenuIndexPassive, + SubmenuIndexActive, + SubmenuIndexTargetedPassive, + SubmenuIndexTargetedActive, + SubmenuIndexChannelPassive, + SubmenuIndexChannelActive, +}; + +static void wifi_marauder_scene_sniffpmkid_options_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + + app->is_custom_tx_string = false; // this will be set if needed by text input + switch(index) { + case SubmenuIndexPassive: + app->selected_tx_string = "sniffpmkid"; + scene_manager_set_scene_state( + app->scene_manager, WifiMarauderSceneSniffPmkidOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneConsoleOutput); + break; + case SubmenuIndexActive: + app->selected_tx_string = "sniffpmkid -d"; + scene_manager_set_scene_state( + app->scene_manager, WifiMarauderSceneSniffPmkidOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneConsoleOutput); + break; + case SubmenuIndexTargetedPassive: + app->selected_tx_string = "sniffpmkid -l"; + scene_manager_set_scene_state( + app->scene_manager, WifiMarauderSceneSniffPmkidOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneConsoleOutput); + break; + case SubmenuIndexTargetedActive: + app->selected_tx_string = "sniffpmkid -d -l"; + scene_manager_set_scene_state( + app->scene_manager, WifiMarauderSceneSniffPmkidOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneConsoleOutput); + break; + case SubmenuIndexChannelPassive: + app->selected_tx_string = "sniffpmkid -c"; + scene_manager_set_scene_state( + app->scene_manager, WifiMarauderSceneSniffPmkidOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneTextInput); + break; + case SubmenuIndexChannelActive: + app->selected_tx_string = "sniffpmkid -d -c"; + scene_manager_set_scene_state( + app->scene_manager, WifiMarauderSceneSniffPmkidOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneTextInput); + break; + } +} + +void wifi_marauder_scene_sniffpmkid_options_on_enter(void* context) { + WifiMarauderApp* app = context; + + Submenu* submenu = app->submenu; + + submenu_set_header(submenu, "Sniff PMKID"); + submenu_add_item( + submenu, + "Passive", + SubmenuIndexPassive, + wifi_marauder_scene_sniffpmkid_options_callback, + app); + submenu_add_item( + submenu, + "Active (Force Deauth)", + SubmenuIndexActive, + wifi_marauder_scene_sniffpmkid_options_callback, + app); + submenu_add_item( + submenu, + "Targeted Passive (List)", + SubmenuIndexTargetedPassive, + wifi_marauder_scene_sniffpmkid_options_callback, + app); + submenu_add_item( + submenu, + "Targeted Active (List)", + SubmenuIndexTargetedActive, + wifi_marauder_scene_sniffpmkid_options_callback, + app); + submenu_add_item( + submenu, + "On Channel # - Passive", + SubmenuIndexChannelPassive, + wifi_marauder_scene_sniffpmkid_options_callback, + app); + submenu_add_item( + submenu, + "On Channel # - Active", + SubmenuIndexChannelActive, + wifi_marauder_scene_sniffpmkid_options_callback, + app); + + submenu_set_selected_item( + submenu, + scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneSniffPmkidOptions)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); +} + +bool wifi_marauder_scene_sniffpmkid_options_on_event(void* context, SceneManagerEvent event) { + //WifiMarauderApp* app = context; + UNUSED(context); + UNUSED(event); + bool consumed = false; + + return consumed; +} + +void wifi_marauder_scene_sniffpmkid_options_on_exit(void* context) { + WifiMarauderApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_start.c b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_start.c index 042ae3224..68c1fab5c 100644 --- a/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_start.c +++ b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_start.c @@ -68,6 +68,14 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = { NO_ARGS, FOCUS_CONSOLE_END, SHOW_STOPSCAN_TIP}, + /*{"Wardrive", {""}, 1, {"wardrive"}, NO_ARGS, FOCUS_CONSOLE_END, SHOW_STOPSCAN_TIP},*/ // No GPS for Wardrive on Mayhem + {"Evil Portal", + {"start"}, + 1, + {"evilportal -c start"}, + NO_ARGS, + FOCUS_CONSOLE_END, + SHOW_STOPSCAN_TIP}, {"Targeted Deauth", {"station", "manual"}, 2, @@ -83,11 +91,10 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = { FOCUS_CONSOLE_END, SHOW_STOPSCAN_TIP}, {"Sniff", - {"beacon", "deauth", "esp", "pmkid", "probe", "pwn", "raw", "bt", "skim"}, - 9, + {"beacon", "deauth", "pmkid", "probe", "pwn", "raw", "bt", "skim"}, + 8, {"sniffbeacon", "sniffdeauth", - "sniffesp", "sniffpmkid", "sniffprobe", "sniffpwn", @@ -97,13 +104,7 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = { NO_ARGS, FOCUS_CONSOLE_END, SHOW_STOPSCAN_TIP}, - {"Sniff PMKID on channel", - {""}, - 1, - {"sniffpmkid -c"}, - INPUT_ARGS, - FOCUS_CONSOLE_END, - SHOW_STOPSCAN_TIP}, + {"Signal Monitor", {""}, 1, {"sigmon"}, NO_ARGS, FOCUS_CONSOLE_END, SHOW_STOPSCAN_TIP}, {"Channel", {"get", "set"}, 2, @@ -111,7 +112,27 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = { TOGGLE_ARGS, FOCUS_CONSOLE_END, NO_TIP}, - {"Camera", + /*{"LED", + {"hex", "pattern"}, + 2, + {"led -s", "led -p"}, + INPUT_ARGS, + FOCUS_CONSOLE_END, + NO_TIP},*/ // No led on mayhem + /*{"GPS Data", + {"stream", "fix", "sats", "lat", "lon", "alt", "date"}, + 7, + {"gpsdata", + "gps -g fix", + "gps -g sat", + "gps -g lat", + "gps -g lon", + "gps -g alt", + "gps -g date"}, + NO_ARGS, + FOCUS_CONSOLE_END, + NO_TIP},*/ // No GPS on Mayhem + {"Camera", {"photo", "flashlight"}, 2, {"photo", "flashlight"}, @@ -119,21 +140,22 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = { FOCUS_CONSOLE_END, NO_TIP}, {"Settings", - {"display", "restore", "ForcePMKID", "ForceProbe", "SavePCAP", "EnableLED", "other"}, - 7, + {"display", "restore", "ForcePMKID", "ForceProbe", "SavePCAP", /*"EnableLED",*/ "other"}, + 6, {"settings", "settings -r", "settings -s ForcePMKID enable", "settings -s ForceProbe enable", "settings -s SavePCAP enable", - "settings -s EnableLED enable", + /*"settings -s EnableLED enable",*/ "settings -s"}, TOGGLE_ARGS, FOCUS_CONSOLE_START, NO_TIP}, - {"Update", {"ota", "sd"}, 2, {"update -w", "update -s"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, + {"Update", {"sd"}, 1, {"update -s"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, {"Reboot", {""}, 1, {"reboot"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, {"Help", {""}, 1, {"help"}, NO_ARGS, FOCUS_CONSOLE_START, SHOW_STOPSCAN_TIP}, + {"Scripts", {""}, 1, {""}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, {"Save to flipper sdcard", // keep as last entry or change logic in callback below {""}, 1, @@ -150,13 +172,6 @@ static void wifi_marauder_scene_start_var_list_enter_callback(void* context, uin furi_assert(index < NUM_MENU_ITEMS); const WifiMarauderItem* item = &items[index]; - if(index == NUM_MENU_ITEMS - 1) { - // "Save to flipper sdcard" special case - start SettingsInit widget - view_dispatcher_send_custom_event( - app->view_dispatcher, WifiMarauderEventStartSettingsInit); - return; - } - const int selected_option_index = app->selected_option_index[index]; furi_assert(selected_option_index < item->num_options_menu); app->selected_tx_string = item->actual_commands[selected_option_index]; @@ -174,6 +189,28 @@ static void wifi_marauder_scene_start_var_list_enter_callback(void* context, uin return; } + if(app->selected_tx_string && + strncmp("sniffpmkid", app->selected_tx_string, strlen("sniffpmkid")) == 0) { + // sniffpmkid submenu + view_dispatcher_send_custom_event( + app->view_dispatcher, WifiMarauderEventStartSniffPmkidOptions); + return; + } + + // Select automation script + if(index == NUM_MENU_ITEMS - 2) { + view_dispatcher_send_custom_event( + app->view_dispatcher, WifiMarauderEventStartScriptSelect); + return; + } + + if(index == NUM_MENU_ITEMS - 1) { + // "Save to flipper sdcard" special case - start SettingsInit widget + view_dispatcher_send_custom_event( + app->view_dispatcher, WifiMarauderEventStartSettingsInit); + return; + } + bool needs_keyboard = (item->needs_keyboard == TOGGLE_ARGS) ? (selected_option_index != 0) : item->needs_keyboard; if(needs_keyboard) { @@ -249,6 +286,14 @@ bool wifi_marauder_scene_start_on_event(void* context, SceneManagerEvent event) scene_manager_set_scene_state( app->scene_manager, WifiMarauderSceneStart, app->selected_menu_index); scene_manager_next_scene(app->scene_manager, WifiMarauderSceneLogViewer); + } else if(event.event == WifiMarauderEventStartScriptSelect) { + scene_manager_set_scene_state( + app->scene_manager, WifiMarauderSceneStart, app->selected_menu_index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptSelect); + } else if(event.event == WifiMarauderEventStartSniffPmkidOptions) { + scene_manager_set_scene_state( + app->scene_manager, WifiMarauderSceneStart, app->selected_menu_index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneSniffPmkidOptions); } consumed = true; } else if(event.type == SceneManagerEventTypeTick) { diff --git a/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_user_input.c b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_user_input.c new file mode 100644 index 000000000..37e544cbe --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/scenes/wifi_marauder_scene_user_input.c @@ -0,0 +1,157 @@ +#include "../wifi_marauder_app_i.h" + +bool wifi_marauder_scene_user_input_validator_number_callback( + const char* text, + FuriString* error, + void* context) { + UNUSED(context); + for(int i = 0; text[i] != '\0'; i++) { + if(text[i] < '0' || text[i] > '9') { + furi_string_printf(error, "This is not\na valid\nnumber!"); + return false; + } + } + return true; +} + +bool wifi_marauder_scene_user_input_validator_file_callback( + const char* text, + FuriString* error, + void* context) { + UNUSED(context); + if(strlen(text) == 0) { + furi_string_printf(error, "File name\ncannot be\nblank!"); + return false; + } + return true; +} + +void wifi_marauder_scene_user_input_ok_callback(void* context) { + WifiMarauderApp* app = context; + + File* file = NULL; + char* file_path = NULL; + + switch(app->user_input_type) { + // Writes the string value of the reference + case WifiMarauderUserInputTypeString: + if(app->user_input_string_reference != NULL) { + strncpy( + *app->user_input_string_reference, + app->text_input_store, + strlen(app->text_input_store) + 1); + app->user_input_string_reference = NULL; + } + break; + // Writes the numerical value of the reference + case WifiMarauderUserInputTypeNumber: + if(app->user_input_number_reference != NULL) { + *app->user_input_number_reference = atoi(app->text_input_store); + app->user_input_number_reference = NULL; + } + break; + // Creates a file with the name entered by the user, if it does not exist + case WifiMarauderUserInputTypeFileName: + file = storage_file_alloc(app->storage); + // Use application directory if not specified + if(app->user_input_file_dir == NULL) { + app->user_input_file_dir = strdup(MARAUDER_APP_FOLDER); + } + if(app->user_input_file_extension != NULL) { + size_t file_path_len = strlen(app->user_input_file_dir) + + strlen(app->text_input_store) + + strlen(app->user_input_file_extension) + 3; + file_path = (char*)malloc(file_path_len); + snprintf( + file_path, + file_path_len, + "%s/%s.%s", + app->user_input_file_dir, + app->text_input_store, + app->user_input_file_extension); + } else { + size_t file_path_len = + strlen(app->user_input_file_dir) + strlen(app->text_input_store) + 2; + file_path = (char*)malloc(file_path_len); + snprintf( + file_path, file_path_len, "%s/%s", app->user_input_file_dir, app->text_input_store); + } + if(storage_file_open(file, file_path, FSAM_WRITE, FSOM_CREATE_NEW)) { + storage_file_close(file); + } + // Free memory + free(app->user_input_file_dir); + app->user_input_file_dir = NULL; + free(app->user_input_file_extension); + app->user_input_file_extension = NULL; + free(file_path); + storage_file_free(file); + break; + default: + break; + } + + scene_manager_previous_scene(app->scene_manager); +} + +void wifi_marauder_scene_user_input_on_enter(void* context) { + WifiMarauderApp* app = context; + + switch(app->user_input_type) { + // Loads the string value of the reference + case WifiMarauderUserInputTypeString: + text_input_set_header_text(app->text_input, "Enter value:"); + text_input_set_validator(app->text_input, NULL, app); + if(app->user_input_string_reference != NULL) { + strncpy( + app->text_input_store, + *app->user_input_string_reference, + strlen(*app->user_input_string_reference) + 1); + } + break; + // Loads the numerical value of the reference + case WifiMarauderUserInputTypeNumber: + text_input_set_header_text(app->text_input, "Enter a valid number:"); + text_input_set_validator( + app->text_input, wifi_marauder_scene_user_input_validator_number_callback, app); + if(app->user_input_number_reference != NULL) { + char number_str[32]; + snprintf(number_str, sizeof(number_str), "%d", *app->user_input_number_reference); + strncpy(app->text_input_store, number_str, strlen(number_str) + 1); + } + break; + // File name + case WifiMarauderUserInputTypeFileName: + text_input_set_header_text(app->text_input, "Enter file name:"); + text_input_set_validator( + app->text_input, wifi_marauder_scene_user_input_validator_file_callback, app); + break; + default: + scene_manager_previous_scene(app->scene_manager); + return; + } + + text_input_set_result_callback( + app->text_input, + wifi_marauder_scene_user_input_ok_callback, + app, + app->text_input_store, + WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE, + false); + + text_input_add_illegal_symbols(app->text_input); + + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewTextInput); +} + +bool wifi_marauder_scene_user_input_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_user_input_on_exit(void* context) { + WifiMarauderApp* app = context; + memset(app->text_input_store, 0, sizeof(app->text_input_store)); + text_input_reset(app->text_input); +} diff --git a/applications/external/esp32cam_marauder_companion/script/cJSON.c b/applications/external/esp32cam_marauder_companion/script/cJSON.c new file mode 100644 index 000000000..06341fe38 --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/cJSON.c @@ -0,0 +1,2743 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning(push) +/* disable warning about single line comments in system headers */ +#pragma warning(disable : 4001) +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "cJSON.h" + +/* define our own boolean type */ +#ifdef true +#undef true +#endif +#define true ((cJSON_bool)1) + +#ifdef false +#undef false +#endif +#define false ((cJSON_bool)0) + +/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ +#ifndef isinf +#define isinf(d) (isnan((d - d)) && !isnan(d)) +#endif +#ifndef isnan +#define isnan(d) (d != d) +#endif + +#ifndef NAN +#ifdef _WIN32 +#define NAN sqrt(-1.0) +#else +#define NAN 0.0 / 0.0 +#endif +#endif + +typedef struct { + const unsigned char* json; + size_t position; +} error; +static error global_error = {NULL, 0}; + +CJSON_PUBLIC(const char*) cJSON_GetErrorPtr(void) { + return (const char*)(global_error.json + global_error.position); +} + +CJSON_PUBLIC(char*) cJSON_GetStringValue(const cJSON* const item) { + if(!cJSON_IsString(item)) { + return NULL; + } + + return item->valuestring; +} + +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON* const item) { + if(!cJSON_IsNumber(item)) { + return (double)NAN; + } + + return item->valuedouble; +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if(CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 15) +#error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) { + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char* string1, const unsigned char* string2) { + if((string1 == NULL) || (string2 == NULL)) { + return 1; + } + + if(string1 == string2) { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) { + if(*string1 == '\0') { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks { + void*(CJSON_CDECL* allocate)(size_t size); + void(CJSON_CDECL* deallocate)(void* pointer); + void*(CJSON_CDECL* reallocate)(void* pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ +static void* CJSON_CDECL internal_malloc(size_t size) { + return malloc(size); +} +static void CJSON_CDECL internal_free(void* pointer) { + free(pointer); +} +static void* CJSON_CDECL internal_realloc(void* pointer, size_t size) { + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +/* strlen of character literals resolved at compile time */ +#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) + +static internal_hooks global_hooks = {internal_malloc, internal_free, internal_realloc}; + +static unsigned char* + cJSON_strdup(const unsigned char* string, const internal_hooks* const hooks) { + size_t length = 0; + unsigned char* copy = NULL; + + if(string == NULL) { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if(copy == NULL) { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) { + if(hooks == NULL) { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if(hooks->malloc_fn != NULL) { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if(hooks->free_fn != NULL) { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON* cJSON_New_Item(const internal_hooks* const hooks) { + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if(node) { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON* item) { + cJSON* next = NULL; + while(item != NULL) { + next = item->next; + if(!(item->type & cJSON_IsReference) && (item->child != NULL)) { + cJSON_Delete(item->child); + } + if(!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) { + global_hooks.deallocate(item->valuestring); + } + if(!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) { +#ifdef ENABLE_LOCALES + struct lconv* lconv = localeconv(); + return (unsigned char)lconv->decimal_point[0]; +#else + return '.'; +#endif +} + +typedef struct { + const unsigned char* content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) \ + ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) \ + ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Converts an array of characters to double. Alternative implementation of strtod() */ +double string_to_double(const char* str, char** endptr) { + double result = 0.0; + int sign = 1; + const char* p = str; + + while(isspace((unsigned char)*p)) p++; + + if(*p == '-') { + sign = -1; + p++; + } else if(*p == '+') { + p++; + } + + while(isdigit((unsigned char)*p)) { + result = result * (double)(10) + ((double)(*p - '0')); + p++; + } + + if(*p == '.') { + double fraction = 0.1; + p++; + + while(isdigit((unsigned char)p[0])) { + fraction *= 0.1L; + result += (p++[0] - '0') * fraction; + } + } + + if(*p == 'e' || *p == 'E') { + int exponent = 0; + int exp_sign = 1; + p++; + + if(*p == '-') { + exp_sign = -1; + p++; + } else if(*p == '+') { + p++; + } + + while(isdigit((unsigned char)*p)) { + exponent = exponent * 10 + (*p - '0'); + p++; + } + + exponent *= exp_sign; + result *= pow(10, exponent); + } + + *endptr = (char*)p; + + return sign * result; +} + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON* const item, parse_buffer* const input_buffer) { + double number = 0; + unsigned char* after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if((input_buffer == NULL) || (input_buffer->content == NULL)) { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for string_to_double) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for(i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) { + switch(buffer_at_offset(input_buffer)[i]) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = string_to_double((const char*)number_c_string, (char**)&after_end); + if(number_c_string == after_end) { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if(number >= INT_MAX) { + item->valueint = INT_MAX; + } else if(number <= (double)INT_MIN) { + item->valueint = INT_MIN; + } else { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON* object, double number) { + if(number >= INT_MAX) { + object->valueint = INT_MAX; + } else if(number <= (double)INT_MIN) { + object->valueint = INT_MIN; + } else { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON* object, const char* valuestring) { + char* copy = NULL; + /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ + if(!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) { + return NULL; + } + if(strlen(valuestring) <= strlen(object->valuestring)) { + strcpy(object->valuestring, valuestring); + return object->valuestring; + } + copy = (char*)cJSON_strdup((const unsigned char*)valuestring, &global_hooks); + if(copy == NULL) { + return NULL; + } + if(object->valuestring != NULL) { + cJSON_free(object->valuestring); + } + object->valuestring = copy; + + return copy; +} + +typedef struct { + unsigned char* buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer* const p, size_t needed) { + unsigned char* newbuffer = NULL; + size_t newsize = 0; + + if((p == NULL) || (p->buffer == NULL)) { + return NULL; + } + + if((p->length > 0) && (p->offset >= p->length)) { + /* make sure that offset is valid */ + return NULL; + } + + if(needed > INT_MAX) { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if(needed <= p->length) { + return p->buffer + p->offset; + } + + if(p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if(needed > (INT_MAX / 2)) { + /* overflow of int, use INT_MAX if possible */ + if(needed <= INT_MAX) { + newsize = INT_MAX; + } else { + return NULL; + } + } else { + newsize = needed * 2; + } + + if(p->hooks.reallocate != NULL) { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if(newbuffer == NULL) { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } else { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if(!newbuffer) { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + + memcpy(newbuffer, p->buffer, p->offset + 1); + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer* const buffer) { + const unsigned char* buffer_pointer = NULL; + if((buffer == NULL) || (buffer->buffer == NULL)) { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* securely comparison of floating-point variables */ +static cJSON_bool compare_double(double a, double b) { + double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + return (fabs(a - b) <= maxVal * DBL_EPSILON); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON* const item, printbuffer* const output_buffer) { + unsigned char* output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test = 0.0; + + if(output_buffer == NULL) { + return false; + } + + /* This checks for NaN and Infinity */ + if(isnan(d) || isinf(d)) { + length = snprintf((char*)number_buffer, sizeof(number_buffer), "null"); + } else { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = snprintf((char*)number_buffer, sizeof(number_buffer), "%1.15g", d); + + /* Check whether the original double can be recovered */ + if((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) { + /* If not, print with 17 decimal places of precision */ + length = snprintf((char*)number_buffer, sizeof(number_buffer), "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occurred */ + if((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if(output_pointer == NULL) { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for(i = 0; i < ((size_t)length); i++) { + if(number_buffer[i] == decimal_point) { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char* const input) { + unsigned int h = 0; + size_t i = 0; + + for(i = 0; i < 4; i++) { + /* parse digit */ + if((input[i] >= '0') && (input[i] <= '9')) { + h += (unsigned int)input[i] - '0'; + } else if((input[i] >= 'A') && (input[i] <= 'F')) { + h += (unsigned int)10 + input[i] - 'A'; + } else if((input[i] >= 'a') && (input[i] <= 'f')) { + h += (unsigned int)10 + input[i] - 'a'; + } else /* invalid */ + { + return 0; + } + + if(i < 3) { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8( + const unsigned char* const input_pointer, + const unsigned char* const input_end, + unsigned char** output_pointer) { + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char* first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if((input_end - first_sequence) < 6) { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if(((first_code >= 0xDC00) && (first_code <= 0xDFFF))) { + goto fail; + } + + /* UTF16 surrogate pair */ + if((first_code >= 0xD800) && (first_code <= 0xDBFF)) { + const unsigned char* second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if((input_end - second_sequence) < 6) { + /* input ends unexpectedly */ + goto fail; + } + + if((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if((second_code < 0xDC00) || (second_code > 0xDFFF)) { + /* invalid second half of the surrogate pair */ + goto fail; + } + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } else { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if(codepoint < 0x80) { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } else if(codepoint < 0x800) { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } else if(codepoint < 0x10000) { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } else if(codepoint <= 0x10FFFF) { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } else { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for(utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if(utf8_length > 1) { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } else { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON* const item, parse_buffer* const input_buffer) { + const unsigned char* input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char* input_end = buffer_at_offset(input_buffer) + 1; + unsigned char* output_pointer = NULL; + unsigned char* output = NULL; + + /* not a string */ + if(buffer_at_offset(input_buffer)[0] != '\"') { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while(((size_t)(input_end - input_buffer->content) < input_buffer->length) && + (*input_end != '\"')) { + /* is escape sequence */ + if(input_end[0] == '\\') { + if((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if(((size_t)(input_end - input_buffer->content) >= input_buffer->length) || + (*input_end != '\"')) { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t)(input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if(output == NULL) { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while(input_pointer < input_end) { + if(*input_pointer != '\\') { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else { + unsigned char sequence_length = 2; + if((input_end - input_pointer) < 1) { + goto fail; + } + + switch(input_pointer[1]) { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if(sequence_length == 0) { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t)(input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if(output != NULL) { + input_buffer->hooks.deallocate(output); + } + + if(input_pointer != NULL) { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool + print_string_ptr(const unsigned char* const input, printbuffer* const output_buffer) { + const unsigned char* input_pointer = NULL; + unsigned char* output = NULL; + unsigned char* output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if(output_buffer == NULL) { + return false; + } + + /* empty string */ + if(input == NULL) { + output = ensure(output_buffer, sizeof("\"\"")); + if(output == NULL) { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for(input_pointer = input; *input_pointer; input_pointer++) { + switch(*input_pointer) { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if(*input_pointer < 32) { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if(output == NULL) { + return false; + } + + /* no characters have to be escaped */ + if(escape_characters == 0) { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for(input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) { + if((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) { + /* normal character, copy */ + *output_pointer = *input_pointer; + } else { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch(*input_pointer) { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + snprintf((char*)output_pointer, 6, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON* const item, printbuffer* const p) { + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON* const item, parse_buffer* const input_buffer); +static cJSON_bool print_value(const cJSON* const item, printbuffer* const output_buffer); +static cJSON_bool parse_array(cJSON* const item, parse_buffer* const input_buffer); +static cJSON_bool print_array(const cJSON* const item, printbuffer* const output_buffer); +static cJSON_bool parse_object(cJSON* const item, parse_buffer* const input_buffer); +static cJSON_bool print_object(const cJSON* const item, printbuffer* const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer* buffer_skip_whitespace(parse_buffer* const buffer) { + if((buffer == NULL) || (buffer->content == NULL)) { + return NULL; + } + + if(cannot_access_at_index(buffer, 0)) { + return buffer; + } + + while(can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) { + buffer->offset++; + } + + if(buffer->offset == buffer->length) { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer* skip_utf8_bom(parse_buffer* const buffer) { + if((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) { + return NULL; + } + + if(can_access_at_index(buffer, 4) && + (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) { + buffer->offset += 3; + } + + return buffer; +} + +CJSON_PUBLIC(cJSON*) +cJSON_ParseWithOpts( + const char* value, + const char** return_parse_end, + cJSON_bool require_null_terminated) { + size_t buffer_length; + + if(NULL == value) { + return NULL; + } + + /* Adding null character size due to require_null_terminated. */ + buffer_length = strlen(value) + sizeof(""); + + return cJSON_ParseWithLengthOpts( + value, buffer_length, return_parse_end, require_null_terminated); +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON*) +cJSON_ParseWithLengthOpts( + const char* value, + size_t buffer_length, + const char** return_parse_end, + cJSON_bool require_null_terminated) { + parse_buffer buffer = {0, 0, 0, 0, {0, 0, 0}}; + cJSON* item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if(value == NULL || 0 == buffer_length) { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = buffer_length; + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if(item == NULL) /* memory fail */ + { + goto fail; + } + + if(!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if(require_null_terminated) { + buffer_skip_whitespace(&buffer); + if((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') { + goto fail; + } + } + if(return_parse_end) { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if(item != NULL) { + cJSON_Delete(item); + } + + if(value != NULL) { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if(buffer.offset < buffer.length) { + local_error.position = buffer.offset; + } else if(buffer.length > 0) { + local_error.position = buffer.length - 1; + } + + if(return_parse_end != NULL) { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON*) cJSON_Parse(const char* value) { + return cJSON_ParseWithOpts(value, 0, 0); +} + +CJSON_PUBLIC(cJSON*) cJSON_ParseWithLength(const char* value, size_t buffer_length) { + return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); +} + +#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) + +static unsigned char* + print(const cJSON* const item, cJSON_bool format, const internal_hooks* const hooks) { + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char* printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*)hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if(buffer->buffer == NULL) { + goto fail; + } + + /* print the value */ + if(!print_value(item, buffer)) { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if(hooks->reallocate != NULL) { + printed = (unsigned char*)hooks->reallocate(buffer->buffer, buffer->offset + 1); + if(printed == NULL) { + goto fail; + } + buffer->buffer = NULL; + } else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*)hooks->allocate(buffer->offset + 1); + if(printed == NULL) { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if(buffer->buffer != NULL) { + hooks->deallocate(buffer->buffer); + } + + if(printed != NULL) { + hooks->deallocate(printed); + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char*) cJSON_Print(const cJSON* item) { + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char*) cJSON_PrintUnformatted(const cJSON* item) { + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char*) cJSON_PrintBuffered(const cJSON* item, int prebuffer, cJSON_bool fmt) { + printbuffer p = {0, 0, 0, 0, 0, 0, {0, 0, 0}}; + + if(prebuffer < 0) { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if(!p.buffer) { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if(!print_value(item, &p)) { + global_hooks.deallocate(p.buffer); + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_PrintPreallocated(cJSON* item, char* buffer, const int length, const cJSON_bool format) { + printbuffer p = {0, 0, 0, 0, 0, 0, {0, 0, 0}}; + + if((length < 0) || (buffer == NULL)) { + return false; + } + + p.buffer = (unsigned char*)buffer; + p.length = (size_t)length; + p.offset = 0; + p.noalloc = true; + p.format = format; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON* const item, parse_buffer* const input_buffer) { + if((input_buffer == NULL) || (input_buffer->content == NULL)) { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if(can_read(input_buffer, 4) && + (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if(can_read(input_buffer, 5) && + (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if(can_read(input_buffer, 4) && + (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) { + return parse_string(item, input_buffer); + } + /* number */ + if(can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || + ((buffer_at_offset(input_buffer)[0] >= '0') && + (buffer_at_offset(input_buffer)[0] <= '9')))) { + return parse_number(item, input_buffer); + } + /* array */ + if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) { + return parse_array(item, input_buffer); + } + /* object */ + if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) { + return parse_object(item, input_buffer); + } + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON* const item, printbuffer* const output_buffer) { + unsigned char* output = NULL; + + if((item == NULL) || (output_buffer == NULL)) { + return false; + } + + switch((item->type) & 0xFF) { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if(output == NULL) { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if(output == NULL) { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if(output == NULL) { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: { + size_t raw_length = 0; + if(item->valuestring == NULL) { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if(output == NULL) { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON* const item, parse_buffer* const input_buffer) { + cJSON* head = NULL; /* head of the linked list */ + cJSON* current_item = NULL; + + if(input_buffer->depth >= CJSON_NESTING_LIMIT) { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if(buffer_at_offset(input_buffer)[0] != '[') { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if(cannot_access_at_index(input_buffer, 0)) { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do { + /* allocate next item */ + cJSON* new_item = cJSON_New_Item(&(input_buffer->hooks)); + if(new_item == NULL) { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if(head == NULL) { + /* start the linked list */ + current_item = head = new_item; + } else { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if(!parse_value(current_item, input_buffer)) { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } while(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if(cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + if(head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if(head != NULL) { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON* const item, printbuffer* const output_buffer) { + unsigned char* output_pointer = NULL; + size_t length = 0; + cJSON* current_element = item->child; + + if(output_buffer == NULL) { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if(output_pointer == NULL) { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while(current_element != NULL) { + if(!print_value(current_element, output_buffer)) { + return false; + } + update_offset(output_buffer); + if(current_element->next) { + length = (size_t)(output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if(output_pointer == NULL) { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if(output_pointer == NULL) { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON* const item, parse_buffer* const input_buffer) { + cJSON* head = NULL; /* linked list head */ + cJSON* current_item = NULL; + + if(input_buffer->depth >= CJSON_NESTING_LIMIT) { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if(cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if(cannot_access_at_index(input_buffer, 0)) { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do { + /* allocate next item */ + cJSON* new_item = cJSON_New_Item(&(input_buffer->hooks)); + if(new_item == NULL) { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if(head == NULL) { + /* start the linked list */ + current_item = head = new_item; + } else { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if(!parse_string(current_item, input_buffer)) { + goto fail; /* failed to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if(cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if(!parse_value(current_item, input_buffer)) { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } while(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if(cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + if(head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if(head != NULL) { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON* const item, printbuffer* const output_buffer) { + unsigned char* output_pointer = NULL; + size_t length = 0; + cJSON* current_item = item->child; + + if(output_buffer == NULL) { + return false; + } + + /* Compose the output: */ + length = (size_t)(output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if(output_pointer == NULL) { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if(output_buffer->format) { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while(current_item) { + if(output_buffer->format) { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if(output_pointer == NULL) { + return false; + } + for(i = 0; i < output_buffer->depth; i++) { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if(!print_string_ptr((unsigned char*)current_item->string, output_buffer)) { + return false; + } + update_offset(output_buffer); + + length = (size_t)(output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if(output_pointer == NULL) { + return false; + } + *output_pointer++ = ':'; + if(output_buffer->format) { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if(!print_value(current_item, output_buffer)) { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if(output_pointer == NULL) { + return false; + } + if(current_item->next) { + *output_pointer++ = ','; + } + + if(output_buffer->format) { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if(output_pointer == NULL) { + return false; + } + if(output_buffer->format) { + size_t i; + for(i = 0; i < (output_buffer->depth - 1); i++) { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON* array) { + cJSON* child = NULL; + size_t size = 0; + + if(array == NULL) { + return 0; + } + + child = array->child; + + while(child != NULL) { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON* array, size_t index) { + cJSON* current_child = NULL; + + if(array == NULL) { + return NULL; + } + + current_child = array->child; + while((current_child != NULL) && (index > 0)) { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON*) cJSON_GetArrayItem(const cJSON* array, int index) { + if(index < 0) { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON* get_object_item( + const cJSON* const object, + const char* const name, + const cJSON_bool case_sensitive) { + cJSON* current_element = NULL; + + if((object == NULL) || (name == NULL)) { + return NULL; + } + + current_element = object->child; + if(case_sensitive) { + while((current_element != NULL) && (current_element->string != NULL) && + (strcmp(name, current_element->string) != 0)) { + current_element = current_element->next; + } + } else { + while((current_element != NULL) && + (case_insensitive_strcmp( + (const unsigned char*)name, (const unsigned char*)(current_element->string)) != + 0)) { + current_element = current_element->next; + } + } + + if((current_element == NULL) || (current_element->string == NULL)) { + return NULL; + } + + return current_element; +} + +CJSON_PUBLIC(cJSON*) cJSON_GetObjectItem(const cJSON* const object, const char* const string) { + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON*) +cJSON_GetObjectItemCaseSensitive(const cJSON* const object, const char* const string) { + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON* object, const char* string) { + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON* prev, cJSON* item) { + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON* create_reference(const cJSON* item, const internal_hooks* const hooks) { + cJSON* reference = NULL; + if(item == NULL) { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if(reference == NULL) { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON* array, cJSON* item) { + cJSON* child = NULL; + + if((item == NULL) || (array == NULL) || (array == item)) { + return false; + } + + child = array->child; + /* + * To find the last item in array quickly, we use prev in array + */ + if(child == NULL) { + /* list is empty, start new one */ + array->child = item; + item->prev = item; + item->next = NULL; + } else { + /* append to the end */ + if(child->prev) { + suffix_object(child->prev, item); + array->child->prev = item; + } + } + + return true; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON* array, cJSON* item) { + return add_item_to_array(array, item); +} + +#if defined(__clang__) || \ + (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) +#pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) { + return (void*)string; +} +#if defined(__clang__) || \ + (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) +#pragma GCC diagnostic pop +#endif + +static cJSON_bool add_item_to_object( + cJSON* const object, + const char* const string, + cJSON* const item, + const internal_hooks* const hooks, + const cJSON_bool constant_key) { + char* new_key = NULL; + int new_type = cJSON_Invalid; + + if((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) { + return false; + } + + if(constant_key) { + new_key = (char*)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } else { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if(new_key == NULL) { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if(!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON* object, const char* string, cJSON* item) { + return add_item_to_object(object, string, item, &global_hooks, false); +} + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON* object, const char* string, cJSON* item) { + return add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON* array, cJSON* item) { + if(array == NULL) { + return false; + } + + return add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_AddItemReferenceToObject(cJSON* object, const char* string, cJSON* item) { + if((object == NULL) || (string == NULL)) { + return false; + } + + return add_item_to_object( + object, string, create_reference(item, &global_hooks), &global_hooks, false); +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON* const object, const char* const name) { + cJSON* null = cJSON_CreateNull(); + if(add_item_to_object(object, name, null, &global_hooks, false)) { + return null; + } + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON* const object, const char* const name) { + cJSON* true_item = cJSON_CreateTrue(); + if(add_item_to_object(object, name, true_item, &global_hooks, false)) { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON* const object, const char* const name) { + cJSON* false_item = cJSON_CreateFalse(); + if(add_item_to_object(object, name, false_item, &global_hooks, false)) { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) +cJSON_AddBoolToObject(cJSON* const object, const char* const name, const cJSON_bool boolean) { + cJSON* bool_item = cJSON_CreateBool(boolean); + if(add_item_to_object(object, name, bool_item, &global_hooks, false)) { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) +cJSON_AddNumberToObject(cJSON* const object, const char* const name, const double number) { + cJSON* number_item = cJSON_CreateNumber(number); + if(add_item_to_object(object, name, number_item, &global_hooks, false)) { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) +cJSON_AddStringToObject(cJSON* const object, const char* const name, const char* const string) { + cJSON* string_item = cJSON_CreateString(string); + if(add_item_to_object(object, name, string_item, &global_hooks, false)) { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) +cJSON_AddRawToObject(cJSON* const object, const char* const name, const char* const raw) { + cJSON* raw_item = cJSON_CreateRaw(raw); + if(add_item_to_object(object, name, raw_item, &global_hooks, false)) { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON* const object, const char* const name) { + cJSON* object_item = cJSON_CreateObject(); + if(add_item_to_object(object, name, object_item, &global_hooks, false)) { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON* const object, const char* const name) { + cJSON* array = cJSON_CreateArray(); + if(add_item_to_object(object, name, array, &global_hooks, false)) { + return array; + } + + cJSON_Delete(array); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_DetachItemViaPointer(cJSON* parent, cJSON* const item) { + if((parent == NULL) || (item == NULL)) { + return NULL; + } + + if(item != parent->child) { + /* not the first element */ + item->prev->next = item->next; + } + if(item->next != NULL) { + /* not the last element */ + item->next->prev = item->prev; + } + + if(item == parent->child) { + /* first element */ + parent->child = item->next; + } else if(item->next == NULL) { + /* last element */ + parent->child->prev = item->prev; + } + + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromArray(cJSON* array, int which) { + if(which < 0) { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON* array, int which) { + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromObject(cJSON* object, const char* string) { + cJSON* to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromObjectCaseSensitive(cJSON* object, const char* string) { + cJSON* to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON* object, const char* string) { + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON* object, const char* string) { + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON* array, int which, cJSON* newitem) { + cJSON* after_inserted = NULL; + + if(which < 0) { + return false; + } + + after_inserted = get_array_item(array, (size_t)which); + if(after_inserted == NULL) { + return add_item_to_array(array, newitem); + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if(after_inserted == array->child) { + array->child = newitem; + } else { + newitem->prev->next = newitem; + } + return true; +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemViaPointer(cJSON* const parent, cJSON* const item, cJSON* replacement) { + if((parent == NULL) || (replacement == NULL) || (item == NULL)) { + return false; + } + + if(replacement == item) { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if(replacement->next != NULL) { + replacement->next->prev = replacement; + } + if(parent->child == item) { + if(parent->child->prev == parent->child) { + replacement->prev = replacement; + } + parent->child = replacement; + } else { /* + * To find the last item in array quickly, we use prev in array. + * We can't modify the last item's next pointer where this item was the parent's child + */ + if(replacement->prev != NULL) { + replacement->prev->next = replacement; + } + if(replacement->next == NULL) { + parent->child->prev = replacement; + } + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON* array, int which, cJSON* newitem) { + if(which < 0) { + return false; + } + + return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object( + cJSON* object, + const char* string, + cJSON* replacement, + cJSON_bool case_sensitive) { + if((replacement == NULL) || (string == NULL)) { + return false; + } + + /* replace the name in the replacement */ + if(!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; + + return cJSON_ReplaceItemViaPointer( + object, get_object_item(object, string, case_sensitive), replacement); +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemInObject(cJSON* object, const char* string, cJSON* newitem) { + return replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemInObjectCaseSensitive(cJSON* object, const char* string, cJSON* newitem) { + return replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON*) cJSON_CreateNull(void) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateTrue(void) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateFalse(void) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateBool(cJSON_bool boolean) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = boolean ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateNumber(double num) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if(num >= INT_MAX) { + item->valueint = INT_MAX; + } else if(num <= (double)INT_MIN) { + item->valueint = INT_MIN; + } else { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateString(const char* string) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateStringReference(const char* string) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item != NULL) { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateObjectReference(const cJSON* child) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateArrayReference(const cJSON* child) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateRaw(const char* raw) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateArray(void) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateObject(void) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON*) cJSON_CreateIntArray(const int* numbers, int count) { + size_t i = 0; + cJSON* n = NULL; + cJSON* p = NULL; + cJSON* a = NULL; + + if((count < 0) || (numbers == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateNumber(numbers[i]); + if(!n) { + cJSON_Delete(a); + return NULL; + } + if(!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + if(a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateFloatArray(const float* numbers, int count) { + size_t i = 0; + cJSON* n = NULL; + cJSON* p = NULL; + cJSON* a = NULL; + + if((count < 0) || (numbers == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) { + cJSON_Delete(a); + return NULL; + } + if(!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + if(a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateDoubleArray(const double* numbers, int count) { + size_t i = 0; + cJSON* n = NULL; + cJSON* p = NULL; + cJSON* a = NULL; + + if((count < 0) || (numbers == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateNumber(numbers[i]); + if(!n) { + cJSON_Delete(a); + return NULL; + } + if(!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + if(a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateStringArray(const char* const* strings, int count) { + size_t i = 0; + cJSON* n = NULL; + cJSON* p = NULL; + cJSON* a = NULL; + + if((count < 0) || (strings == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateString(strings[i]); + if(!n) { + cJSON_Delete(a); + return NULL; + } + if(!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + if(a && a->child) { + a->child->prev = n; + } + + return a; +} + +/* Duplication */ +CJSON_PUBLIC(cJSON*) cJSON_Duplicate(const cJSON* item, cJSON_bool recurse) { + cJSON* newitem = NULL; + cJSON* child = NULL; + cJSON* next = NULL; + cJSON* newchild = NULL; + + /* Bail on bad ptr */ + if(!item) { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if(!newitem) { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if(item->valuestring) { + newitem->valuestring = + (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if(!newitem->valuestring) { + goto fail; + } + } + if(item->string) { + newitem->string = (item->type & cJSON_StringIsConst) ? + item->string : + (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if(!newitem->string) { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if(!recurse) { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while(child != NULL) { + newchild = cJSON_Duplicate( + child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if(!newchild) { + goto fail; + } + if(next != NULL) { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } else { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + if(newitem && newitem->child) { + newitem->child->prev = newchild; + } + + return newitem; + +fail: + if(newitem != NULL) { + cJSON_Delete(newitem); + } + + return NULL; +} + +static void skip_oneline_comment(char** input) { + *input += static_strlen("//"); + + for(; (*input)[0] != '\0'; ++(*input)) { + if((*input)[0] == '\n') { + *input += static_strlen("\n"); + return; + } + } +} + +static void skip_multiline_comment(char** input) { + *input += static_strlen("/*"); + + for(; (*input)[0] != '\0'; ++(*input)) { + if(((*input)[0] == '*') && ((*input)[1] == '/')) { + *input += static_strlen("*/"); + return; + } + } +} + +static void minify_string(char** input, char** output) { + (*output)[0] = (*input)[0]; + *input += static_strlen("\""); + *output += static_strlen("\""); + + for(; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { + (*output)[0] = (*input)[0]; + + if((*input)[0] == '\"') { + (*output)[0] = '\"'; + *input += static_strlen("\""); + *output += static_strlen("\""); + return; + } else if(((*input)[0] == '\\') && ((*input)[1] == '\"')) { + (*output)[1] = (*input)[1]; + *input += static_strlen("\""); + *output += static_strlen("\""); + } + } +} + +CJSON_PUBLIC(void) cJSON_Minify(char* json) { + char* into = json; + + if(json == NULL) { + return; + } + + while(json[0] != '\0') { + switch(json[0]) { + case ' ': + case '\t': + case '\r': + case '\n': + json++; + break; + + case '/': + if(json[1] == '/') { + skip_oneline_comment(&json); + } else if(json[1] == '*') { + skip_multiline_comment(&json); + } else { + json++; + } + break; + + case '\"': + minify_string(&json, (char**)&into); + break; + + default: + into[0] = json[0]; + json++; + into++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_Compare(const cJSON* const a, const cJSON* const b, const cJSON_bool case_sensitive) { + if((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) { + return false; + } + + /* check if type is valid */ + switch(a->type & 0xFF) { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if(a == b) { + return true; + } + + switch(a->type & 0xFF) { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if(compare_double(a->valuedouble, b->valuedouble)) { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if((a->valuestring == NULL) || (b->valuestring == NULL)) { + return false; + } + if(strcmp(a->valuestring, b->valuestring) == 0) { + return true; + } + + return false; + + case cJSON_Array: { + cJSON* a_element = a->child; + cJSON* b_element = b->child; + + for(; (a_element != NULL) && (b_element != NULL);) { + if(!cJSON_Compare(a_element, b_element, case_sensitive)) { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if(a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: { + cJSON* a_element = NULL; + cJSON* b_element = NULL; + cJSON_ArrayForEach(a_element, a) { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if(b_element == NULL) { + return false; + } + + if(!cJSON_Compare(a_element, b_element, case_sensitive)) { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) { + a_element = get_object_item(a, b_element->string, case_sensitive); + if(a_element == NULL) { + return false; + } + + if(!cJSON_Compare(b_element, a_element, case_sensitive)) { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void*) cJSON_malloc(size_t size) { + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void* object) { + global_hooks.deallocate(object); +} diff --git a/applications/external/esp32cam_marauder_companion/script/cJSON.h b/applications/external/esp32cam_marauder_companion/script/cJSON.h new file mode 100644 index 000000000..14ec83d9d --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/cJSON.h @@ -0,0 +1,321 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(__WINDOWS__) && \ + (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif + +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +#define CJSON_CDECL __cdecl +#define CJSON_STDCALL __stdcall + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && \ + !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type CJSON_STDCALL +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#endif +#else /* !__WINDOWS__ */ +#define CJSON_CDECL +#define CJSON_STDCALL + +#if(defined(__GNUC__) || defined(__SUNPRO_CC) || defined(__SUNPRO_C)) && \ + defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 15 + +#include + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON { + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON* next; + struct cJSON* prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON* child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char* valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char* string; +} cJSON; + +typedef struct cJSON_Hooks { + /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ + void*(CJSON_CDECL* malloc_fn)(size_t sz); + void(CJSON_CDECL* free_fn)(void* ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON*) cJSON_Parse(const char* value); +CJSON_PUBLIC(cJSON*) cJSON_ParseWithLength(const char* value, size_t buffer_length); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON*) +cJSON_ParseWithOpts( + const char* value, + const char** return_parse_end, + cJSON_bool require_null_terminated); +CJSON_PUBLIC(cJSON*) +cJSON_ParseWithLengthOpts( + const char* value, + size_t buffer_length, + const char** return_parse_end, + cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char*) cJSON_Print(const cJSON* item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char*) cJSON_PrintUnformatted(const cJSON* item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char*) cJSON_PrintBuffered(const cJSON* item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) +cJSON_PrintPreallocated(cJSON* item, char* buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON* item); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON* array); +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON*) cJSON_GetArrayItem(const cJSON* array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON*) cJSON_GetObjectItem(const cJSON* const object, const char* const string); +CJSON_PUBLIC(cJSON*) +cJSON_GetObjectItemCaseSensitive(const cJSON* const object, const char* const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON* object, const char* string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char*) cJSON_GetErrorPtr(void); + +/* Check item type and return its value */ +CJSON_PUBLIC(char*) cJSON_GetStringValue(const cJSON* const item); +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON* const item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON* const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON*) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON*) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON*) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON*) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON*) cJSON_CreateString(const char* string); +/* raw json */ +CJSON_PUBLIC(cJSON*) cJSON_CreateRaw(const char* raw); +CJSON_PUBLIC(cJSON*) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON*) cJSON_CreateObject(void); + +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON*) cJSON_CreateStringReference(const char* string); +/* Create an object/array that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON*) cJSON_CreateObjectReference(const cJSON* child); +CJSON_PUBLIC(cJSON*) cJSON_CreateArrayReference(const cJSON* child); + +/* These utilities create an Array of count items. + * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ +CJSON_PUBLIC(cJSON*) cJSON_CreateIntArray(const int* numbers, int count); +CJSON_PUBLIC(cJSON*) cJSON_CreateFloatArray(const float* numbers, int count); +CJSON_PUBLIC(cJSON*) cJSON_CreateDoubleArray(const double* numbers, int count); +CJSON_PUBLIC(cJSON*) cJSON_CreateStringArray(const char* const* strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON* array, cJSON* item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON* object, const char* string, cJSON* item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON* object, const char* string, cJSON* item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON* array, cJSON* item); +CJSON_PUBLIC(cJSON_bool) +cJSON_AddItemReferenceToObject(cJSON* object, const char* string, cJSON* item); + +/* Remove/Detach items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON*) cJSON_DetachItemViaPointer(cJSON* parent, cJSON* const item); +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromArray(cJSON* array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON* array, int which); +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromObject(cJSON* object, const char* string); +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromObjectCaseSensitive(cJSON* object, const char* string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON* object, const char* string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON* object, const char* string); + +/* Update array items. */ +CJSON_PUBLIC(cJSON_bool) +cJSON_InsertItemInArray( + cJSON* array, + int which, + cJSON* newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemViaPointer(cJSON* const parent, cJSON* const item, cJSON* replacement); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON* array, int which, cJSON* newitem); +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemInObject(cJSON* object, const char* string, cJSON* newitem); +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemInObjectCaseSensitive(cJSON* object, const char* string, cJSON* newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON*) cJSON_Duplicate(const cJSON* item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will + * need to be released. With recurse!=0, it will duplicate any children connected to the item. + * The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) +cJSON_Compare(const cJSON* const a, const cJSON* const b, const cJSON_bool case_sensitive); + +/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. + * The input pointer json cannot point to a read-only address area, such as a string constant, + * but should point to a readable and writable address area. */ +CJSON_PUBLIC(void) cJSON_Minify(char* json); + +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON* const object, const char* const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON* const object, const char* const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON* const object, const char* const name); +CJSON_PUBLIC(cJSON*) +cJSON_AddBoolToObject(cJSON* const object, const char* const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) +cJSON_AddNumberToObject(cJSON* const object, const char* const name, const double number); +CJSON_PUBLIC(cJSON*) +cJSON_AddStringToObject(cJSON* const object, const char* const name, const char* const string); +CJSON_PUBLIC(cJSON*) +cJSON_AddRawToObject(cJSON* const object, const char* const name, const char* const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON* const object, const char* const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON* const object, const char* const name); + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) \ + ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON* object, double number); +#define cJSON_SetNumberValue(object, number) \ + ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) +/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON* object, const char* valuestring); + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) \ + for(element = (array != NULL) ? (array)->child : NULL; element != NULL; \ + element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void*) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void* object); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu.c b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu.c new file mode 100644 index 000000000..6fe853eb6 --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu.c @@ -0,0 +1,32 @@ +#include "wifi_marauder_script_stage_menu.h" + +WifiMarauderScriptStageMenu* + wifi_marauder_script_stage_menu_create(WifiMarauderScriptStageType stage_type) { + WifiMarauderScriptStageMenu* script_stage_menu = malloc(sizeof(WifiMarauderScriptStageMenu)); + + switch(stage_type) { +#define ADD_STAGE(name, id) \ + case WifiMarauderScriptStageType##id: \ + wifi_marauder_script_stage_menu_##name##_load(script_stage_menu); \ + break; + +#include "wifi_marauder_script_stage_menu_config.h" +#undef ADD_STAGE + } + return script_stage_menu; +} + +void wifi_marauder_script_stage_menu_free(WifiMarauderScriptStageMenu* stage_menu) { + if(stage_menu == NULL) { + return; + } + for(uint32_t i = 0; i < stage_menu->num_items; i++) { + WifiMarauderScriptMenuItem* item = &(stage_menu->items[i]); + for(int j = 0; j < item->num_options; j++) { + free(item->options[j]); + } + free(item->name); + } + free(stage_menu->items); + free(stage_menu); +} diff --git a/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu.h b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu.h new file mode 100644 index 000000000..f5186526c --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include "../wifi_marauder_script.h" + +#define ITEM_EDIT_MAX_OPTIONS (12) + +typedef void (*VariableItemSetupCallback)(VariableItem* item); +typedef void (*VariableItemSelectCallback)(void* context); + +typedef enum WifiMarauderScriptMenuItemType { + WifiMarauderScriptMenuItemTypeString, + WifiMarauderScriptMenuItemTypeNumber, + WifiMarauderScriptMenuItemTypeOptionsString, + WifiMarauderScriptMenuItemTypeOptionsNumber, + WifiMarauderScriptMenuItemTypeListString, + WifiMarauderScriptMenuItemTypeListNumber +} WifiMarauderScriptMenuItemType; + +typedef struct WifiMarauderScriptMenuItem { + char* name; + WifiMarauderScriptMenuItemType type; + int num_options; + char* options[ITEM_EDIT_MAX_OPTIONS]; + VariableItemSetupCallback setup_callback; + VariableItemChangeCallback change_callback; + VariableItemSelectCallback select_callback; +} WifiMarauderScriptMenuItem; + +typedef struct WifiMarauderScriptStageMenu { + WifiMarauderScriptMenuItem* items; + uint32_t num_items; +} WifiMarauderScriptStageMenu; + +#define ADD_STAGE(name, id) \ + void wifi_marauder_script_stage_menu_##name##_load(WifiMarauderScriptStageMenu*); +#include "wifi_marauder_script_stage_menu_config.h" +#undef ADD_STAGE + +WifiMarauderScriptStageMenu* + wifi_marauder_script_stage_menu_create(WifiMarauderScriptStageType stage_type); +void wifi_marauder_script_stage_menu_free(WifiMarauderScriptStageMenu* list); diff --git a/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconap.c b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconap.c new file mode 100644 index 000000000..35a74ee3d --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconap.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_beaconap_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageBeaconAp* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_beaconap_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageBeaconAp* stage_beaconap = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_beaconap->timeout; +} + +void wifi_marauder_script_stage_menu_beaconap_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = "Timeout", + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_beaconap_stage_timeout_setup_callback, + .select_callback = wifi_marauder_beaconap_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconlist.c b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconlist.c new file mode 100644 index 000000000..6f320db3e --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconlist.c @@ -0,0 +1,59 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_beaconlist_stage_ssids_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageBeaconList* stage_beaconlist = app->script_edit_selected_stage->stage; + app->script_stage_edit_strings_reference = &stage_beaconlist->ssids; + app->script_stage_edit_string_count_reference = &stage_beaconlist->ssid_count; +} + +void wifi_marauder_beaconlist_stage_random_ssids_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageBeaconList* stage = app->script_edit_selected_stage->stage; + char random_ssids_str[32]; + snprintf(random_ssids_str, sizeof(random_ssids_str), "%d", stage->random_ssids); + variable_item_set_current_value_text(item, random_ssids_str); +} + +void wifi_marauder_beaconlist_stage_random_ssids_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageBeaconList* stage_beaconlist = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_beaconlist->random_ssids; +} + +void wifi_marauder_beaconlist_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageBeaconList* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_beaconlist_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageBeaconList* stage_beaconlist = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_beaconlist->timeout; +} + +void wifi_marauder_script_stage_menu_beaconlist_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 3; + stage_menu->items = malloc(3 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("SSIDs"), + .type = WifiMarauderScriptMenuItemTypeListString, + .num_options = 1, + .select_callback = wifi_marauder_beaconlist_stage_ssids_select_callback}; + stage_menu->items[1] = (WifiMarauderScriptMenuItem){ + .name = strdup("Generate random"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_beaconlist_stage_random_ssids_setup_callback, + .select_callback = wifi_marauder_beaconlist_stage_random_ssids_select_callback}; + stage_menu->items[2] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_beaconlist_stage_timeout_setup_callback, + .select_callback = wifi_marauder_beaconlist_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_config.h b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_config.h new file mode 100644 index 000000000..1fd2a314b --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_config.h @@ -0,0 +1,14 @@ +ADD_STAGE(scan, Scan) +ADD_STAGE(select, Select) +ADD_STAGE(deauth, Deauth) +ADD_STAGE(probe, Probe) +ADD_STAGE(sniffraw, SniffRaw) +ADD_STAGE(sniffbeacon, SniffBeacon) +ADD_STAGE(sniffdeauth, SniffDeauth) +ADD_STAGE(sniffesp, SniffEsp) +ADD_STAGE(sniffpmkid, SniffPmkid) +ADD_STAGE(sniffpwn, SniffPwn) +ADD_STAGE(beaconlist, BeaconList) +ADD_STAGE(beaconap, BeaconAp) +ADD_STAGE(exec, Exec) +ADD_STAGE(delay, Delay) \ No newline at end of file diff --git a/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_deauth.c b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_deauth.c new file mode 100644 index 000000000..b15b6f461 --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_deauth.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_deauth_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageDeauth* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_deauth_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageDeauth* stage_deauth = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_deauth->timeout; +} + +void wifi_marauder_script_stage_menu_deauth_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_deauth_stage_timeout_setup_callback, + .select_callback = wifi_marauder_deauth_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_delay.c b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_delay.c new file mode 100644 index 000000000..ffd74f720 --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_delay.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_delay_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageDelay* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_delay_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageDelay* stage_delay = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_delay->timeout; +} + +void wifi_marauder_script_stage_menu_delay_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_delay_stage_timeout_setup_callback, + .select_callback = wifi_marauder_delay_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_exec.c b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_exec.c new file mode 100644 index 000000000..62afdc2f3 --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_exec.c @@ -0,0 +1,30 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_exec_stage_filter_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageExec* stage = app->script_edit_selected_stage->stage; + if(stage->command != NULL) { + variable_item_set_current_value_text(item, stage->command); + } +} + +void wifi_marauder_exec_stage_filter_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageExec* stage_select = app->script_edit_selected_stage->stage; + if(stage_select->command == NULL) { + stage_select->command = malloc(128); + } + app->user_input_string_reference = &stage_select->command; +} + +void wifi_marauder_script_stage_menu_exec_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Command"), + .type = WifiMarauderScriptMenuItemTypeString, + .num_options = 1, + .setup_callback = wifi_marauder_exec_stage_filter_setup_callback, + .select_callback = wifi_marauder_exec_stage_filter_select_callback}; +} \ No newline at end of file diff --git a/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_probe.c b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_probe.c new file mode 100644 index 000000000..53fa26f47 --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_probe.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_probe_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageProbe* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_probe_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageProbe* stage_probe = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_probe->timeout; +} + +void wifi_marauder_script_stage_menu_probe_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_probe_stage_timeout_setup_callback, + .select_callback = wifi_marauder_probe_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_scan.c b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_scan.c new file mode 100644 index 000000000..3aab740bb --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_scan.c @@ -0,0 +1,93 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_scan_stage_type_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage; + variable_item_set_current_value_index(item, stage->type); +} + +void wifi_marauder_scan_stage_type_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + // Get menu item + uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->var_item_list); + const WifiMarauderScriptMenuItem* menu_item = + &app->script_stage_menu->items[current_stage_index]; + + // Defines the text of the selected option + uint8_t option_index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, menu_item->options[option_index]); + + // Updates the attribute value of the current stage + WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage; + stage->type = option_index; +} + +void wifi_marauder_scan_stage_channel_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage; + if(stage->channel >= 0 && stage->channel < 12) { + variable_item_set_current_value_index(item, stage->channel); + } else { + variable_item_set_current_value_index(item, 0); + } +} + +void wifi_marauder_scan_stage_channel_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + // Get menu item + uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->var_item_list); + const WifiMarauderScriptMenuItem* menu_item = + &app->script_stage_menu->items[current_stage_index]; + + // Defines the text of the selected option + uint8_t option_index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, menu_item->options[option_index]); + + // Updates the attribute value of the current stage + WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage; + stage->channel = option_index; +} + +void wifi_marauder_scan_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_scan_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageScan* stage_scan = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_scan->timeout; +} + +void wifi_marauder_script_stage_menu_scan_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 3; + stage_menu->items = malloc(3 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Type"), + .type = WifiMarauderScriptMenuItemTypeOptionsString, + .num_options = 2, + .options = {"ap", "station"}, + .setup_callback = wifi_marauder_scan_stage_type_setup_callback, + .change_callback = wifi_marauder_scan_stage_type_change_callback, + }; + stage_menu->items[1] = (WifiMarauderScriptMenuItem){ + .name = strdup("Channel"), + .type = WifiMarauderScriptMenuItemTypeOptionsNumber, + .num_options = 12, + .options = {"none", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"}, + .setup_callback = wifi_marauder_scan_stage_channel_setup_callback, + .change_callback = wifi_marauder_scan_stage_channel_change_callback, + }; + stage_menu->items[2] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_scan_stage_timeout_setup_callback, + .select_callback = wifi_marauder_scan_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_select.c b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_select.c new file mode 100644 index 000000000..a6121db95 --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_select.c @@ -0,0 +1,95 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_select_stage_type_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSelect* stage = app->script_edit_selected_stage->stage; + variable_item_set_current_value_index(item, stage->type); +} + +void wifi_marauder_select_stage_type_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + // Get menu item + uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->var_item_list); + const WifiMarauderScriptMenuItem* menu_item = + &app->script_stage_menu->items[current_stage_index]; + + // Defines the text of the selected option + uint8_t option_index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, menu_item->options[option_index]); + + // Updates the attribute value of the current stage + WifiMarauderScriptStageSelect* stage = app->script_edit_selected_stage->stage; + stage->type = option_index; +} + +void wifi_marauder_select_stage_filter_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSelect* stage = app->script_edit_selected_stage->stage; + + if(stage->filter != NULL) { + variable_item_set_current_value_index(item, 0); + variable_item_set_current_value_text(item, stage->filter); + } else { + variable_item_set_current_value_index(item, 1); + } +} + +void wifi_marauder_select_stage_filter_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSelect* stage = app->script_edit_selected_stage->stage; + + // Clears the filter if you change the option. Flipper input box does not accept blank text + if(variable_item_get_current_value_index(item) == 1) { + stage->filter = NULL; + variable_item_set_current_value_index(item, 0); + variable_item_set_values_count(item, 1); + } + + if(stage->filter != NULL) { + variable_item_set_current_value_text(item, stage->filter); + } else { + variable_item_set_current_value_text(item, ""); + } +} + +void wifi_marauder_select_stage_filter_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSelect* stage_select = app->script_edit_selected_stage->stage; + if(stage_select->filter == NULL) { + stage_select->filter = malloc(128); + } + app->user_input_string_reference = &stage_select->filter; +} + +void wifi_marauder_select_stage_indexes_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSelect* stage_select = app->script_edit_selected_stage->stage; + app->script_stage_edit_numbers_reference = &stage_select->indexes; + app->script_stage_edit_number_count_reference = &stage_select->index_count; +} + +void wifi_marauder_script_stage_menu_select_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 3; + stage_menu->items = malloc(3 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Type"), + .type = WifiMarauderScriptMenuItemTypeOptionsString, + .num_options = 2, + .options = {"ap", "station"}, + .setup_callback = wifi_marauder_select_stage_type_setup_callback, + .change_callback = wifi_marauder_select_stage_type_change_callback}; + stage_menu->items[1] = (WifiMarauderScriptMenuItem){ + .name = strdup("Filter"), + .type = WifiMarauderScriptMenuItemTypeString, + .num_options = 2, + .setup_callback = wifi_marauder_select_stage_filter_setup_callback, + .change_callback = wifi_marauder_select_stage_filter_change_callback, + .select_callback = wifi_marauder_select_stage_filter_select_callback}; + stage_menu->items[2] = (WifiMarauderScriptMenuItem){ + .name = strdup("Indexes"), + .type = WifiMarauderScriptMenuItemTypeListNumber, + .num_options = 1, + .select_callback = wifi_marauder_select_stage_indexes_select_callback}; +} \ No newline at end of file diff --git a/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffbeacon.c b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffbeacon.c new file mode 100644 index 000000000..11e7b3297 --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffbeacon.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_sniffbeacon_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffBeacon* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_sniffbeacon_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffBeacon* stage_sniffbeacon = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffbeacon->timeout; +} + +void wifi_marauder_script_stage_menu_sniffbeacon_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffbeacon_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffbeacon_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffdeauth.c b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffdeauth.c new file mode 100644 index 000000000..935a55936 --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffdeauth.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_sniffdeauth_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffDeauth* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_sniffdeauth_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffDeauth* stage_sniffdeauth = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffdeauth->timeout; +} + +void wifi_marauder_script_stage_menu_sniffdeauth_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffdeauth_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffdeauth_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffesp.c b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffesp.c new file mode 100644 index 000000000..e90d6b06c --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffesp.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_sniffesp_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffEsp* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_sniffesp_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffEsp* stage_sniffesp = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffesp->timeout; +} + +void wifi_marauder_script_stage_menu_sniffesp_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffesp_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffesp_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpmkid.c b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpmkid.c new file mode 100644 index 000000000..6a591c4e0 --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpmkid.c @@ -0,0 +1,118 @@ +#include "../../wifi_marauder_app_i.h" + +static void wifi_marauder_sniffpmkid_stage_hop_channels_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + variable_item_set_current_value_index(item, stage->hop_channels); +} + +static void wifi_marauder_sniffpmkid_stage_hop_channels_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->var_item_list); + const WifiMarauderScriptMenuItem* menu_item = + &app->script_stage_menu->items[current_stage_index]; + + uint8_t option_index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, menu_item->options[option_index]); + + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + stage->hop_channels = option_index; +} + +static void wifi_marauder_sniffpmkid_stage_force_deauth_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + variable_item_set_current_value_index(item, stage->force_deauth); +} + +static void wifi_marauder_sniffpmkid_stage_force_deauth_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + // Get menu item + uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->var_item_list); + const WifiMarauderScriptMenuItem* menu_item = + &app->script_stage_menu->items[current_stage_index]; + + // Defines the text of the selected option + uint8_t option_index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, menu_item->options[option_index]); + + // Updates the attribute value of the current stage + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + stage->force_deauth = option_index; +} + +static void wifi_marauder_sniffpmkid_stage_channel_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + if(stage->channel >= 0 && stage->channel < 12) { + variable_item_set_current_value_index(item, stage->channel); + } else { + variable_item_set_current_value_index(item, 0); + } +} + +static void wifi_marauder_sniffpmkid_stage_channel_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + // Get menu item + uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->var_item_list); + const WifiMarauderScriptMenuItem* menu_item = + &app->script_stage_menu->items[current_stage_index]; + + // Defines the text of the selected option + uint8_t option_index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, menu_item->options[option_index]); + + // Updates the attribute value of the current stage + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + stage->channel = option_index; +} + +static void wifi_marauder_sniffpmkid_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +static void wifi_marauder_sniffpmkid_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffPmkid* stage_sniffpmkid = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffpmkid->timeout; +} + +void wifi_marauder_script_stage_menu_sniffpmkid_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 4; + stage_menu->items = malloc(4 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Force deauth"), + .type = WifiMarauderScriptMenuItemTypeOptionsString, + .num_options = 2, + .options = {"no", "yes"}, + .setup_callback = wifi_marauder_sniffpmkid_stage_force_deauth_setup_callback, + .change_callback = wifi_marauder_sniffpmkid_stage_force_deauth_change_callback}; + stage_menu->items[1] = (WifiMarauderScriptMenuItem){ + .name = strdup("Channel"), + .type = WifiMarauderScriptMenuItemTypeOptionsNumber, + .num_options = 12, + .options = {"none", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"}, + .setup_callback = wifi_marauder_sniffpmkid_stage_channel_setup_callback, + .change_callback = wifi_marauder_sniffpmkid_stage_channel_change_callback}; + stage_menu->items[2] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffpmkid_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffpmkid_stage_timeout_select_callback}; + stage_menu->items[3] = (WifiMarauderScriptMenuItem){ + .name = strdup("Hop Channels"), + .type = WifiMarauderScriptMenuItemTypeOptionsString, + .num_options = 2, + .options = {"no", "yes"}, + .setup_callback = wifi_marauder_sniffpmkid_stage_hop_channels_setup_callback, + .change_callback = wifi_marauder_sniffpmkid_stage_hop_channels_change_callback}; +} \ No newline at end of file diff --git a/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpwn.c b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpwn.c new file mode 100644 index 000000000..d0859cd8b --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpwn.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_sniffpwn_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffPwn* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_sniffpwn_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffPwn* stage_sniffpwn = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffpwn->timeout; +} + +void wifi_marauder_script_stage_menu_sniffpwn_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffpwn_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffpwn_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffraw.c b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffraw.c new file mode 100644 index 000000000..39641f1ee --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffraw.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_sniffraw_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffRaw* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_sniffraw_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffRaw* stage_sniffraw = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffraw->timeout; +} + +void wifi_marauder_script_stage_menu_sniffraw_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffraw_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffraw_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/esp32cam_marauder_companion/script/wifi_marauder_script.c b/applications/external/esp32cam_marauder_companion/script/wifi_marauder_script.c new file mode 100644 index 000000000..a33e27cc5 --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/wifi_marauder_script.c @@ -0,0 +1,962 @@ +#include "../wifi_marauder_app_i.h" +#include "wifi_marauder_script.h" + +WifiMarauderScript* wifi_marauder_script_alloc() { + WifiMarauderScript* script = (WifiMarauderScript*)malloc(sizeof(WifiMarauderScript)); + if(script == NULL) { + return NULL; + } + script->name = NULL; + script->description = NULL; + script->first_stage = NULL; + script->last_stage = NULL; + script->enable_led = WifiMarauderScriptBooleanUndefined; + script->save_pcap = WifiMarauderScriptBooleanUndefined; + script->repeat = 1; + return script; +} + +WifiMarauderScript* wifi_marauder_script_create(const char* script_name) { + WifiMarauderScript* script = wifi_marauder_script_alloc(); + script->name = strdup(script_name); + return script; +} + +void _wifi_marauder_script_load_meta(WifiMarauderScript* script, cJSON* meta_section) { + if(meta_section != NULL) { + // Script description + cJSON* description = cJSON_GetObjectItem(meta_section, "description"); + if(description != NULL) { + script->description = strdup(description->valuestring); + } + // Enable LED + cJSON* enable_led_json = cJSON_GetObjectItem(meta_section, "enableLed"); + if(cJSON_IsBool(enable_led_json)) { + script->enable_led = enable_led_json->valueint; + } + // Save PCAP + cJSON* save_pcap_json = cJSON_GetObjectItem(meta_section, "savePcap"); + if(cJSON_IsBool(save_pcap_json)) { + script->save_pcap = save_pcap_json->valueint; + } + // Times the script will be repeated + cJSON* repeat = cJSON_GetObjectItem(meta_section, "repeat"); + if(repeat != NULL) { + script->repeat = repeat->valueint; + } + } + if(script->description == NULL) { + script->description = strdup("My script"); + } +} + +WifiMarauderScriptStageScan* _wifi_marauder_script_get_stage_scan(cJSON* stages) { + cJSON* stage_scan = cJSON_GetObjectItem(stages, "scan"); + if(stage_scan == NULL) { + return NULL; + } + cJSON* type = cJSON_GetObjectItem(stage_scan, "type"); + if(type == NULL) { + return NULL; + } + WifiMarauderScriptScanType scan_type; + if(strcmp(type->valuestring, "ap") == 0) { + scan_type = WifiMarauderScriptScanTypeAp; + } else if(strcmp(type->valuestring, "station") == 0) { + scan_type = WifiMarauderScriptScanTypeStation; + } else { + return NULL; + } + cJSON* channel = cJSON_GetObjectItem(stage_scan, "channel"); + int scan_channel = channel != NULL ? (int)cJSON_GetNumberValue(channel) : 0; + cJSON* timeout = cJSON_GetObjectItem(stage_scan, "timeout"); + int scan_timeout = timeout != NULL ? (int)cJSON_GetNumberValue(timeout) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SCAN; + + WifiMarauderScriptStageScan* scan_stage = + (WifiMarauderScriptStageScan*)malloc(sizeof(WifiMarauderScriptStageScan)); + scan_stage->type = scan_type; + scan_stage->channel = scan_channel; + scan_stage->timeout = scan_timeout; + + return scan_stage; +} + +WifiMarauderScriptStageSelect* _wifi_marauder_script_get_stage_select(cJSON* stages) { + cJSON* select_stage_json = cJSON_GetObjectItemCaseSensitive(stages, "select"); + if(select_stage_json == NULL) { + return NULL; + } + + cJSON* type_json = cJSON_GetObjectItemCaseSensitive(select_stage_json, "type"); + cJSON* filter_json = cJSON_GetObjectItemCaseSensitive(select_stage_json, "filter"); + cJSON* indexes_json = cJSON_GetObjectItemCaseSensitive(select_stage_json, "indexes"); + cJSON* allow_repeat_json = cJSON_GetObjectItemCaseSensitive(select_stage_json, "allow_repeat"); + + if(!cJSON_IsString(type_json)) { + return NULL; + } + WifiMarauderScriptSelectType select_type; + if(strcmp(type_json->valuestring, "ap") == 0) { + select_type = WifiMarauderScriptSelectTypeAp; + } else if(strcmp(type_json->valuestring, "station") == 0) { + select_type = WifiMarauderScriptSelectTypeStation; + } else if(strcmp(type_json->valuestring, "ssid") == 0) { + select_type = WifiMarauderScriptSelectTypeSsid; + } else { + return NULL; + } + char* filter_str = cJSON_IsString(filter_json) ? strdup(filter_json->valuestring) : NULL; + + WifiMarauderScriptStageSelect* stage_select = + (WifiMarauderScriptStageSelect*)malloc(sizeof(WifiMarauderScriptStageSelect)); + stage_select->type = select_type; + stage_select->allow_repeat = cJSON_IsBool(allow_repeat_json) ? allow_repeat_json->valueint : + true; + stage_select->filter = filter_str; + + if(cJSON_IsArray(indexes_json)) { + int indexes_size = cJSON_GetArraySize(indexes_json); + int* indexes = (int*)malloc(indexes_size * sizeof(int)); + for(int i = 0; i < indexes_size; i++) { + cJSON* index_item = cJSON_GetArrayItem(indexes_json, i); + if(cJSON_IsNumber(index_item)) { + indexes[i] = index_item->valueint; + } + } + stage_select->indexes = indexes; + stage_select->index_count = indexes_size; + } else { + stage_select->indexes = NULL; + stage_select->index_count = 0; + } + + return stage_select; +} + +WifiMarauderScriptStageDeauth* _wifi_marauder_script_get_stage_deauth(cJSON* stages) { + cJSON* deauth_stage_json = cJSON_GetObjectItemCaseSensitive(stages, "deauth"); + if(deauth_stage_json == NULL) { + return NULL; + } + + cJSON* timeout = cJSON_GetObjectItem(deauth_stage_json, "timeout"); + int deauth_timeout = timeout != NULL ? (int)cJSON_GetNumberValue(timeout) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_DEAUTH; + + WifiMarauderScriptStageDeauth* deauth_stage = + (WifiMarauderScriptStageDeauth*)malloc(sizeof(WifiMarauderScriptStageDeauth)); + deauth_stage->timeout = deauth_timeout; + + return deauth_stage; +} + +WifiMarauderScriptStageProbe* _wifi_marauder_script_get_stage_probe(cJSON* stages) { + cJSON* probe_stage_json = cJSON_GetObjectItemCaseSensitive(stages, "probe"); + if(probe_stage_json == NULL) { + return NULL; + } + + cJSON* timeout = cJSON_GetObjectItem(probe_stage_json, "timeout"); + int probe_timeout = timeout != NULL ? (int)cJSON_GetNumberValue(timeout) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_PROBE; + + WifiMarauderScriptStageProbe* probe_stage = + (WifiMarauderScriptStageProbe*)malloc(sizeof(WifiMarauderScriptStageProbe)); + probe_stage->timeout = probe_timeout; + + return probe_stage; +} + +WifiMarauderScriptStageSniffRaw* _wifi_marauder_script_get_stage_sniff_raw(cJSON* stages) { + cJSON* sniffraw_stage_json = cJSON_GetObjectItem(stages, "sniffraw"); + if(sniffraw_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(sniffraw_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + WifiMarauderScriptStageSniffRaw* sniff_raw_stage = + (WifiMarauderScriptStageSniffRaw*)malloc(sizeof(WifiMarauderScriptStageSniffRaw)); + sniff_raw_stage->timeout = timeout; + + return sniff_raw_stage; +} + +WifiMarauderScriptStageSniffBeacon* _wifi_marauder_script_get_stage_sniff_beacon(cJSON* stages) { + cJSON* sniffbeacon_stage_json = cJSON_GetObjectItem(stages, "sniffbeacon"); + if(sniffbeacon_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(sniffbeacon_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + WifiMarauderScriptStageSniffBeacon* sniff_beacon_stage = + (WifiMarauderScriptStageSniffBeacon*)malloc(sizeof(WifiMarauderScriptStageSniffBeacon)); + sniff_beacon_stage->timeout = timeout; + + return sniff_beacon_stage; +} + +WifiMarauderScriptStageSniffDeauth* _wifi_marauder_script_get_stage_sniff_deauth(cJSON* stages) { + cJSON* sniffdeauth_stage_json = cJSON_GetObjectItem(stages, "sniffdeauth"); + if(sniffdeauth_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(sniffdeauth_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + WifiMarauderScriptStageSniffDeauth* sniff_deauth_stage = + (WifiMarauderScriptStageSniffDeauth*)malloc(sizeof(WifiMarauderScriptStageSniffDeauth)); + sniff_deauth_stage->timeout = timeout; + + return sniff_deauth_stage; +} + +WifiMarauderScriptStageSniffEsp* _wifi_marauder_script_get_stage_sniff_esp(cJSON* stages) { + cJSON* sniffesp_stage_json = cJSON_GetObjectItem(stages, "sniffesp"); + if(sniffesp_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(sniffesp_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + WifiMarauderScriptStageSniffEsp* sniff_esp_stage = + (WifiMarauderScriptStageSniffEsp*)malloc(sizeof(WifiMarauderScriptStageSniffEsp)); + sniff_esp_stage->timeout = timeout; + + return sniff_esp_stage; +} + +WifiMarauderScriptStageSniffPmkid* _wifi_marauder_script_get_stage_sniff_pmkid(cJSON* stages) { + cJSON* sniffpmkid_stage_json = cJSON_GetObjectItem(stages, "sniffpmkid"); + if(sniffpmkid_stage_json == NULL) { + return NULL; + } + + cJSON* channel_json = cJSON_GetObjectItem(sniffpmkid_stage_json, "channel"); + int channel = channel_json != NULL ? (int)cJSON_GetNumberValue(channel_json) : 0; + + cJSON* timeout_json = cJSON_GetObjectItem(sniffpmkid_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + cJSON* force_deauth_json = + cJSON_GetObjectItemCaseSensitive(sniffpmkid_stage_json, "forceDeauth"); + bool force_deauth = cJSON_IsBool(force_deauth_json) ? force_deauth_json->valueint : true; + + cJSON* hop_channels_json = + cJSON_GetObjectItemCaseSensitive(sniffpmkid_stage_json, "hopChannels"); + bool hop_channels = cJSON_IsBool(hop_channels_json) ? hop_channels_json->valueint : false; + + WifiMarauderScriptStageSniffPmkid* sniff_pmkid_stage = + (WifiMarauderScriptStageSniffPmkid*)malloc(sizeof(WifiMarauderScriptStageSniffPmkid)); + + if(sniff_pmkid_stage == NULL) { + // Handle memory allocation error + return NULL; + } + sniff_pmkid_stage->channel = channel; + sniff_pmkid_stage->timeout = timeout; + sniff_pmkid_stage->force_deauth = force_deauth; + sniff_pmkid_stage->hop_channels = hop_channels; + + return sniff_pmkid_stage; +} + +WifiMarauderScriptStageSniffPwn* _wifi_marauder_script_get_stage_sniff_pwn(cJSON* stages) { + cJSON* sniffpwn_stage_json = cJSON_GetObjectItem(stages, "sniffpwn"); + if(sniffpwn_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(sniffpwn_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + WifiMarauderScriptStageSniffPwn* sniff_pwn_stage = + (WifiMarauderScriptStageSniffPwn*)malloc(sizeof(WifiMarauderScriptStageSniffPwn)); + sniff_pwn_stage->timeout = timeout; + + return sniff_pwn_stage; +} + +WifiMarauderScriptStageBeaconList* _wifi_marauder_script_get_stage_beacon_list(cJSON* stages) { + cJSON* stage_beaconlist = cJSON_GetObjectItem(stages, "beaconList"); + if(stage_beaconlist == NULL) { + return NULL; + } + WifiMarauderScriptStageBeaconList* beaconlist_stage = + (WifiMarauderScriptStageBeaconList*)malloc(sizeof(WifiMarauderScriptStageBeaconList)); + if(beaconlist_stage == NULL) { + return NULL; + } + cJSON* ssids = cJSON_GetObjectItem(stage_beaconlist, "ssids"); + if(ssids == NULL) { + return NULL; + } + // SSID count + int ssid_count = cJSON_GetArraySize(ssids); + if(ssid_count == 0) { + return NULL; + } + beaconlist_stage->ssid_count = ssid_count; + // SSIDs + beaconlist_stage->ssids = (char**)malloc(sizeof(char*) * ssid_count); + if(beaconlist_stage->ssids == NULL) { + return NULL; + } + for(int i = 0; i < ssid_count; i++) { + cJSON* ssid = cJSON_GetArrayItem(ssids, i); + if(ssid == NULL) { + continue; + } + char* ssid_string = cJSON_GetStringValue(ssid); + if(ssid_string == NULL) { + continue; + } + beaconlist_stage->ssids[i] = (char*)malloc(sizeof(char) * (strlen(ssid_string) + 1)); + strcpy(beaconlist_stage->ssids[i], ssid_string); + } + // Timeout + cJSON* timeout = cJSON_GetObjectItem(stage_beaconlist, "timeout"); + beaconlist_stage->timeout = timeout != NULL ? (int)cJSON_GetNumberValue(timeout) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON; + // Random SSIDs + cJSON* random_ssids = cJSON_GetObjectItem(stage_beaconlist, "generate"); + beaconlist_stage->random_ssids = + random_ssids != NULL ? (int)cJSON_GetNumberValue(random_ssids) : 0; + + return beaconlist_stage; +} + +WifiMarauderScriptStageBeaconAp* _wifi_marauder_script_get_stage_beacon_ap(cJSON* stages) { + cJSON* beaconap_stage_json = cJSON_GetObjectItem(stages, "beaconAp"); + if(beaconap_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(beaconap_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON; + + WifiMarauderScriptStageBeaconAp* beacon_ap_stage = + (WifiMarauderScriptStageBeaconAp*)malloc(sizeof(WifiMarauderScriptStageBeaconAp)); + beacon_ap_stage->timeout = timeout; + + return beacon_ap_stage; +} + +WifiMarauderScriptStageExec* _wifi_marauder_script_get_stage_exec(cJSON* stages) { + cJSON* exec_stage_json = cJSON_GetObjectItem(stages, "exec"); + if(exec_stage_json == NULL) { + return NULL; + } + + cJSON* command_json = cJSON_GetObjectItemCaseSensitive(exec_stage_json, "command"); + char* command_str = cJSON_IsString(command_json) ? strdup(command_json->valuestring) : NULL; + + WifiMarauderScriptStageExec* exec_stage = + (WifiMarauderScriptStageExec*)malloc(sizeof(WifiMarauderScriptStageExec)); + exec_stage->command = command_str; + + return exec_stage; +} + +WifiMarauderScriptStageDelay* _wifi_marauder_script_get_stage_delay(cJSON* stages) { + cJSON* delay_stage_json = cJSON_GetObjectItem(stages, "delay"); + if(delay_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(delay_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : 0; + + WifiMarauderScriptStageDelay* delay_stage = + (WifiMarauderScriptStageDelay*)malloc(sizeof(WifiMarauderScriptStageDelay)); + delay_stage->timeout = timeout; + + return delay_stage; +} + +WifiMarauderScriptStage* + _wifi_marauder_script_create_stage(WifiMarauderScriptStageType type, void* stage_data) { + WifiMarauderScriptStage* stage = + (WifiMarauderScriptStage*)malloc(sizeof(WifiMarauderScriptStage)); + stage->type = type; + stage->stage = stage_data; + stage->next_stage = NULL; + return stage; +} + +void wifi_marauder_script_add_stage( + WifiMarauderScript* script, + WifiMarauderScriptStageType stage_type, + void* stage_data) { + if(script == NULL || stage_data == NULL) { + return; + } + WifiMarauderScriptStage* stage = _wifi_marauder_script_create_stage(stage_type, stage_data); + if(script->last_stage != NULL) { + script->last_stage->next_stage = stage; + } else { + script->first_stage = stage; + } + script->last_stage = stage; +} + +void _wifi_marauder_script_load_stages(WifiMarauderScript* script, cJSON* stages) { + // Scan stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeScan, _wifi_marauder_script_get_stage_scan(stages)); + // Select stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeSelect, _wifi_marauder_script_get_stage_select(stages)); + // Deauth stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeDeauth, _wifi_marauder_script_get_stage_deauth(stages)); + // Probe stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeProbe, _wifi_marauder_script_get_stage_probe(stages)); + // Sniff raw stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffRaw, + _wifi_marauder_script_get_stage_sniff_raw(stages)); + // Sniff beacon stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffBeacon, + _wifi_marauder_script_get_stage_sniff_beacon(stages)); + // Sniff deauth stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffDeauth, + _wifi_marauder_script_get_stage_sniff_deauth(stages)); + // Sniff esp stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffEsp, + _wifi_marauder_script_get_stage_sniff_esp(stages)); + // Sniff PMKID stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffPmkid, + _wifi_marauder_script_get_stage_sniff_pmkid(stages)); + // Sniff pwn stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffPwn, + _wifi_marauder_script_get_stage_sniff_pwn(stages)); + // Beacon List stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeBeaconList, + _wifi_marauder_script_get_stage_beacon_list(stages)); + // Beacon Ap stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeBeaconAp, + _wifi_marauder_script_get_stage_beacon_ap(stages)); + // Exec stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeExec, _wifi_marauder_script_get_stage_exec(stages)); + // Delay stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeDelay, _wifi_marauder_script_get_stage_delay(stages)); +} + +WifiMarauderScript* wifi_marauder_script_parse_raw(const char* json_raw) { + WifiMarauderScript* script = wifi_marauder_script_alloc(); + if(script == NULL) { + return NULL; + } + cJSON* json = cJSON_Parse(json_raw); + if(json == NULL) { + return NULL; + } + cJSON* meta = cJSON_GetObjectItem(json, "meta"); + _wifi_marauder_script_load_meta(script, meta); + + cJSON* stages = cJSON_GetObjectItem(json, "stages"); + if(cJSON_IsArray(stages)) { + cJSON* stage_item = NULL; + cJSON_ArrayForEach(stage_item, stages) { + _wifi_marauder_script_load_stages(script, stage_item); + } + } else { + _wifi_marauder_script_load_stages(script, stages); + } + + return script; +} + +WifiMarauderScript* wifi_marauder_script_parse_json(Storage* storage, const char* file_path) { + WifiMarauderScript* script = NULL; + File* script_file = storage_file_alloc(storage); + FuriString* script_name = furi_string_alloc(); + path_extract_filename_no_ext(file_path, script_name); + + if(storage_file_open(script_file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { + uint32_t file_size = storage_file_size(script_file); + char* json_buffer = (char*)malloc(file_size + 1); + uint16_t bytes_read = storage_file_read(script_file, json_buffer, file_size); + json_buffer[bytes_read] = '\0'; + + script = wifi_marauder_script_parse_raw(json_buffer); + } + if(script == NULL) { + script = wifi_marauder_script_create(furi_string_get_cstr(script_name)); + } + script->name = strdup(furi_string_get_cstr(script_name)); + + furi_string_free(script_name); + storage_file_close(script_file); + storage_file_free(script_file); + return script; +} + +cJSON* _wifi_marauder_script_create_json_meta(WifiMarauderScript* script) { + cJSON* meta_json = cJSON_CreateObject(); + if(script->description != NULL) { + cJSON_AddStringToObject(meta_json, "description", script->description); + } else { + cJSON_AddStringToObject(meta_json, "description", "My Script"); + } + if(script->enable_led != WifiMarauderScriptBooleanUndefined) { + cJSON_AddBoolToObject( + meta_json, "enableLed", (script->enable_led == WifiMarauderScriptBooleanTrue)); + } + if(script->save_pcap != WifiMarauderScriptBooleanUndefined) { + cJSON_AddBoolToObject( + meta_json, "savePcap", (script->save_pcap == WifiMarauderScriptBooleanTrue)); + } + cJSON_AddNumberToObject(meta_json, "repeat", script->repeat); + return meta_json; +} + +cJSON* _wifi_marauder_script_create_json_scan(WifiMarauderScriptStageScan* scan_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "scan", cJSON_CreateObject()); + cJSON* scan_json = cJSON_GetObjectItem(stage_json, "scan"); + // Scan type + cJSON_AddStringToObject( + scan_json, "type", scan_stage->type == WifiMarauderScriptScanTypeAp ? "ap" : "station"); + // Channel + if(scan_stage->channel > 0) { + cJSON_AddNumberToObject(scan_json, "channel", scan_stage->channel); + } + // Timeout + if(scan_stage->timeout > 0) { + cJSON_AddNumberToObject(scan_json, "timeout", scan_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_select(WifiMarauderScriptStageSelect* select_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "select", cJSON_CreateObject()); + cJSON* select_json = cJSON_GetObjectItem(stage_json, "select"); + // Select type + cJSON_AddStringToObject( + select_json, + "type", + select_stage->type == WifiMarauderScriptSelectTypeAp ? "ap" : + select_stage->type == WifiMarauderScriptSelectTypeStation ? "station" : + "ssid"); + if(select_stage->filter != NULL) { + cJSON_AddStringToObject(select_json, "filter", select_stage->filter); + } + // Indexes + if(select_stage->indexes != NULL && select_stage->index_count > 0) { + cJSON* indexes_json = cJSON_CreateArray(); + for(int i = 0; i < select_stage->index_count; i++) { + cJSON_AddItemToArray(indexes_json, cJSON_CreateNumber(select_stage->indexes[i])); + } + cJSON_AddItemToObject(select_json, "indexes", indexes_json); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_deauth(WifiMarauderScriptStageDeauth* deauth_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "deauth", cJSON_CreateObject()); + cJSON* deauth_json = cJSON_GetObjectItem(stage_json, "deauth"); + // Timeout + if(deauth_stage->timeout > 0) { + cJSON_AddNumberToObject(deauth_json, "timeout", deauth_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_probe(WifiMarauderScriptStageProbe* probe_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "probe", cJSON_CreateObject()); + cJSON* probe_json = cJSON_GetObjectItem(stage_json, "probe"); + // Timeout + if(probe_stage->timeout > 0) { + cJSON_AddNumberToObject(probe_json, "timeout", probe_stage->timeout); + } + return stage_json; +} + +cJSON* + _wifi_marauder_script_create_json_sniffraw(WifiMarauderScriptStageSniffRaw* sniffraw_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffRaw", cJSON_CreateObject()); + cJSON* sniffraw_json = cJSON_GetObjectItem(stage_json, "sniffRaw"); + // Timeout + if(sniffraw_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffraw_json, "timeout", sniffraw_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_sniffbeacon( + WifiMarauderScriptStageSniffBeacon* sniffbeacon_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffBeacon", cJSON_CreateObject()); + cJSON* sniffbeacon_json = cJSON_GetObjectItem(stage_json, "sniffBeacon"); + // Timeout + if(sniffbeacon_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffbeacon_json, "timeout", sniffbeacon_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_sniffdeauth( + WifiMarauderScriptStageSniffDeauth* sniffdeauth_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffDeauth", cJSON_CreateObject()); + cJSON* sniffdeauth_json = cJSON_GetObjectItem(stage_json, "sniffDeauth"); + // Timeout + if(sniffdeauth_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffdeauth_json, "timeout", sniffdeauth_stage->timeout); + } + return stage_json; +} + +cJSON* + _wifi_marauder_script_create_json_sniffesp(WifiMarauderScriptStageSniffEsp* sniffesp_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffEsp", cJSON_CreateObject()); + cJSON* sniffesp_json = cJSON_GetObjectItem(stage_json, "sniffEsp"); + // Timeout + if(sniffesp_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffesp_json, "timeout", sniffesp_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_sniffpmkid( + WifiMarauderScriptStageSniffPmkid* sniffpmkid_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffPmkid", cJSON_CreateObject()); + cJSON* sniffpmkid_json = cJSON_GetObjectItem(stage_json, "sniffPmkid"); + // Force deauth + cJSON_AddBoolToObject(sniffpmkid_json, "forceDeauth", sniffpmkid_stage->force_deauth); + // Channel + if(sniffpmkid_stage->channel > 0) { + cJSON_AddNumberToObject(sniffpmkid_json, "channel", sniffpmkid_stage->channel); + } + // Timeout + if(sniffpmkid_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffpmkid_json, "timeout", sniffpmkid_stage->timeout); + } + // Hop channels + cJSON_AddBoolToObject(sniffpmkid_json, "hopChannels", sniffpmkid_stage->hop_channels); + + return stage_json; +} + +cJSON* + _wifi_marauder_script_create_json_sniffpwn(WifiMarauderScriptStageSniffPwn* sniffpwn_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffPwn", cJSON_CreateObject()); + cJSON* sniffpwn_json = cJSON_GetObjectItem(stage_json, "sniffPwn"); + // Timeout + if(sniffpwn_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffpwn_json, "timeout", sniffpwn_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_beaconlist( + WifiMarauderScriptStageBeaconList* beaconlist_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "beaconList", cJSON_CreateObject()); + cJSON* beaconlist_json = cJSON_GetObjectItem(stage_json, "beaconList"); + // SSIDs + if(beaconlist_stage->ssids != NULL) { + cJSON* ssids_json = cJSON_CreateStringArray( + (const char**)beaconlist_stage->ssids, beaconlist_stage->ssid_count); + cJSON_AddItemToObject(beaconlist_json, "ssids", ssids_json); + } + // Random SSIDs + if(beaconlist_stage->random_ssids > 0) { + cJSON_AddNumberToObject(beaconlist_json, "generate", beaconlist_stage->random_ssids); + } + // Timeout + if(beaconlist_stage->timeout > 0) { + cJSON_AddNumberToObject(beaconlist_json, "timeout", beaconlist_stage->timeout); + } + return stage_json; +} + +cJSON* + _wifi_marauder_script_create_json_beaconap(WifiMarauderScriptStageBeaconAp* beaconap_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "beaconAp", cJSON_CreateObject()); + cJSON* beaconap_json = cJSON_GetObjectItem(stage_json, "beaconAp"); + // Timeout + if(beaconap_stage->timeout > 0) { + cJSON_AddNumberToObject(beaconap_json, "timeout", beaconap_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_exec(WifiMarauderScriptStageExec* exec_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "exec", cJSON_CreateObject()); + cJSON* exec_json = cJSON_GetObjectItem(stage_json, "exec"); + // Command + cJSON_AddStringToObject( + exec_json, "command", exec_stage->command != NULL ? exec_stage->command : ""); + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_delay(WifiMarauderScriptStageDelay* delay_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "delay", cJSON_CreateObject()); + cJSON* delay_json = cJSON_GetObjectItem(stage_json, "delay"); + // Timeout + if(delay_stage->timeout > 0) { + cJSON_AddNumberToObject(delay_json, "timeout", delay_stage->timeout); + } + return stage_json; +} + +void wifi_marauder_script_save_json( + Storage* storage, + const char* file_path, + WifiMarauderScript* script) { + File* script_file = storage_file_alloc(storage); + + if(storage_file_open(script_file, file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + cJSON* root_json = cJSON_CreateObject(); + + // Meta info + cJSON* meta_json = _wifi_marauder_script_create_json_meta(script); + cJSON_AddItemToObject(root_json, "meta", meta_json); + + // Create array for stages + cJSON* stages_array = cJSON_CreateArray(); + cJSON_AddItemToObject(root_json, "stages", stages_array); + + // Iterate over each stage and create the corresponding JSON object + WifiMarauderScriptStage* stage = script->first_stage; + while(stage != NULL) { + cJSON* stage_json = NULL; + + switch(stage->type) { + case WifiMarauderScriptStageTypeScan: { + WifiMarauderScriptStageScan* scan_stage = + (WifiMarauderScriptStageScan*)stage->stage; + stage_json = _wifi_marauder_script_create_json_scan(scan_stage); + break; + } + case WifiMarauderScriptStageTypeSelect: { + WifiMarauderScriptStageSelect* select_stage = + (WifiMarauderScriptStageSelect*)stage->stage; + stage_json = _wifi_marauder_script_create_json_select(select_stage); + break; + } + case WifiMarauderScriptStageTypeDeauth: { + WifiMarauderScriptStageDeauth* deauth_stage = + (WifiMarauderScriptStageDeauth*)stage->stage; + stage_json = _wifi_marauder_script_create_json_deauth(deauth_stage); + break; + } + case WifiMarauderScriptStageTypeProbe: { + WifiMarauderScriptStageProbe* probe_stage = + (WifiMarauderScriptStageProbe*)stage->stage; + stage_json = _wifi_marauder_script_create_json_probe(probe_stage); + break; + } + case WifiMarauderScriptStageTypeSniffRaw: { + WifiMarauderScriptStageSniffRaw* sniffraw_stage = + (WifiMarauderScriptStageSniffRaw*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffraw(sniffraw_stage); + break; + } + case WifiMarauderScriptStageTypeSniffBeacon: { + WifiMarauderScriptStageSniffBeacon* sniffbeacon_stage = + (WifiMarauderScriptStageSniffBeacon*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffbeacon(sniffbeacon_stage); + break; + } + case WifiMarauderScriptStageTypeSniffDeauth: { + WifiMarauderScriptStageSniffDeauth* sniffdeauth_stage = + (WifiMarauderScriptStageSniffDeauth*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffdeauth(sniffdeauth_stage); + break; + } + case WifiMarauderScriptStageTypeSniffEsp: { + WifiMarauderScriptStageSniffEsp* sniffesp_stage = + (WifiMarauderScriptStageSniffEsp*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffesp(sniffesp_stage); + break; + } + case WifiMarauderScriptStageTypeSniffPmkid: { + WifiMarauderScriptStageSniffPmkid* sniffpmkid_stage = + (WifiMarauderScriptStageSniffPmkid*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffpmkid(sniffpmkid_stage); + break; + } + case WifiMarauderScriptStageTypeSniffPwn: { + WifiMarauderScriptStageSniffPwn* sniffpwn_stage = + (WifiMarauderScriptStageSniffPwn*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffpwn(sniffpwn_stage); + break; + } + case WifiMarauderScriptStageTypeBeaconList: { + WifiMarauderScriptStageBeaconList* beaconlist_stage = + (WifiMarauderScriptStageBeaconList*)stage->stage; + stage_json = _wifi_marauder_script_create_json_beaconlist(beaconlist_stage); + break; + } + case WifiMarauderScriptStageTypeBeaconAp: { + WifiMarauderScriptStageBeaconAp* beaconap_stage = + (WifiMarauderScriptStageBeaconAp*)stage->stage; + stage_json = _wifi_marauder_script_create_json_beaconap(beaconap_stage); + break; + } + case WifiMarauderScriptStageTypeExec: { + WifiMarauderScriptStageExec* exec_stage = + (WifiMarauderScriptStageExec*)stage->stage; + stage_json = _wifi_marauder_script_create_json_exec(exec_stage); + break; + } + case WifiMarauderScriptStageTypeDelay: { + WifiMarauderScriptStageDelay* delay_stage = + (WifiMarauderScriptStageDelay*)stage->stage; + stage_json = _wifi_marauder_script_create_json_delay(delay_stage); + break; + } + } + + // Add the stage JSON object to the "stages" array + if(stage_json != NULL) { + cJSON_AddItemToArray(stages_array, stage_json); + } + + stage = stage->next_stage; + } + + // Write JSON to file + char* json_str = cJSON_Print(root_json); + storage_file_write(script_file, json_str, strlen(json_str)); + + //free(json_str); + storage_file_close(script_file); + } + storage_file_free(script_file); +} + +bool wifi_marauder_script_has_stage( + WifiMarauderScript* script, + WifiMarauderScriptStageType stage_type) { + if(script == NULL) { + return false; + } + WifiMarauderScriptStage* current_stage = script->first_stage; + while(current_stage != NULL) { + if(current_stage->type == stage_type) { + return true; + } + current_stage = current_stage->next_stage; + } + return false; +} + +void wifi_marauder_script_free(WifiMarauderScript* script) { + if(script == NULL) { + return; + } + WifiMarauderScriptStage* current_stage = script->first_stage; + while(current_stage != NULL) { + WifiMarauderScriptStage* next_stage = current_stage->next_stage; + switch(current_stage->type) { + case WifiMarauderScriptStageTypeScan: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSelect: + if(((WifiMarauderScriptStageSelect*)current_stage->stage)->filter != NULL) { + free(((WifiMarauderScriptStageSelect*)current_stage->stage)->filter); + } + if(((WifiMarauderScriptStageSelect*)current_stage->stage)->indexes != NULL) { + free(((WifiMarauderScriptStageSelect*)current_stage->stage)->indexes); + } + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeDeauth: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeProbe: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffRaw: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffBeacon: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffDeauth: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffEsp: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffPmkid: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffPwn: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeBeaconList: + for(int i = 0; + i < ((WifiMarauderScriptStageBeaconList*)current_stage->stage)->ssid_count; + i++) { + free(((WifiMarauderScriptStageBeaconList*)current_stage->stage)->ssids[i]); + } + free(((WifiMarauderScriptStageBeaconList*)current_stage->stage)->ssids); + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeBeaconAp: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeExec: + if(((WifiMarauderScriptStageExec*)current_stage->stage)->command != NULL) { + free(((WifiMarauderScriptStageExec*)current_stage->stage)->command); + } + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeDelay: + free(current_stage->stage); + break; + } + free(current_stage); + current_stage = next_stage; + } + free(script->name); + free(script->description); + free(script); +} \ No newline at end of file diff --git a/applications/external/esp32cam_marauder_companion/script/wifi_marauder_script.h b/applications/external/esp32cam_marauder_companion/script/wifi_marauder_script.h new file mode 100644 index 000000000..2cf52196b --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/wifi_marauder_script.h @@ -0,0 +1,258 @@ +/* + * ---------------------------------------------------------------------------------------------------- + * STEPS TO ADD A NEW STAGE: + * + * wifi_marauder_script.h + * - Complement WifiMarauderScriptStageType enum with new stage + * - Create struct WifiMarauderScriptStage???? for the new stage + * + * wifi_marauder_script.c + * - Change _wifi_marauder_script_load_stages() to load new stage + * - Change wifi_marauder_script_save_json() to support the new stage + * - Add case to free memory in wifi_marauder_script_free() + * + * wifi_marauder_script_executor.c + * - Create function "void _wifi_marauder_script_execute_????(WifiMarauderScriptStage????* stage)" + * - Add case in wifi_marauder_script_execute_stage() + * + * wifi_marauder_scene_script_edit.c + * - Add case in wifi_marauder_scene_script_edit_on_enter() + * + * wifi_marauder_scene_script_stage_add.c + * - Create stage creation function and add in wifi_marauder_scene_script_stage_add_on_enter() + * + * wifi_marauder_script_stage_menu_config.h + * - Add the new stage and implement its functions in a new file + * + * ---------------------------------------------------------------------------------------------------- + * SCRIPT SYNTAX (In order of execution): + * { + * "meta": { + * "description": "My script", + * "repeat": times the script will repeat (default 1), + * "enableLed": true (default) | false, + * "savePcap": true (default) | false + * }, + * "stages": { + * "scan": { + * "type": "ap" | "station", + * "timeout": seconds, + * "channel": 1-11 + * }, + * "select": { + * "type": "ap" | "station" | "ssid", + * "filter": "all" | "contains -f '{SSID fragment}' or equals '{SSID}' or ...", + * "indexes": [0, 1, 2, 3...], + * }, + * "deauth": { + * "timeout": seconds + * }, + * "probe": { + * "timeout": seconds + * }, + * "sniffRaw": { + * "timeout": seconds + * }, + * "sniffBeacon": { + * "timeout": seconds + * }, + * "sniffDeauth": { + * "timeout": seconds + * }, + * "sniffEsp": { + * "timeout": seconds + * }, + * "sniffPmkid": { + * "forceDeauth": true (default) | false, + * "channel": 1-11, + * "timeout": seconds + * }, + * "sniffPwn": { + * "timeout": seconds + * }, + * "beaconList": { + * "ssids": [ + * "SSID 1", + * "SSID 2", + * "SSID 3" + * ], + * "generate": number of random SSIDs that will be generated, + * "timeout": seconds + * } + * "beaconAp": { + * "timeout": seconds + * } + * "exec": { + * "command": Command (eg: "clearlist -a") + * } + * "delay": { + * "timeout": seconds + * } + * } + * } + * + * Note: It is possible to inform "stages" as an array, allowing ordering and repetition of stages of the same type: + * "stages": [ + * { + * "beaconList": { "ssids": ["SSID 1", "SSID 2"] } + * }, + * { + * "beaconList": { "generate": 4 } + * }, + * ] + * ---------------------------------------------------------------------------------------------------- + */ + +#pragma once + +#include +#include "cJSON.h" + +#define WIFI_MARAUDER_DEFAULT_TIMEOUT_SCAN 15 +#define WIFI_MARAUDER_DEFAULT_TIMEOUT_DEAUTH 30 +#define WIFI_MARAUDER_DEFAULT_TIMEOUT_PROBE 60 +#define WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF 60 +#define WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON 60 + +typedef enum { + WifiMarauderScriptBooleanFalse = 0, + WifiMarauderScriptBooleanTrue = 1, + WifiMarauderScriptBooleanUndefined = 2 +} WifiMarauderScriptBoolean; + +typedef enum { + WifiMarauderScriptStageTypeScan, + WifiMarauderScriptStageTypeSelect, + WifiMarauderScriptStageTypeDeauth, + WifiMarauderScriptStageTypeProbe, + WifiMarauderScriptStageTypeSniffRaw, + WifiMarauderScriptStageTypeSniffBeacon, + WifiMarauderScriptStageTypeSniffDeauth, + WifiMarauderScriptStageTypeSniffEsp, + WifiMarauderScriptStageTypeSniffPmkid, + WifiMarauderScriptStageTypeSniffPwn, + WifiMarauderScriptStageTypeBeaconList, + WifiMarauderScriptStageTypeBeaconAp, + WifiMarauderScriptStageTypeExec, + WifiMarauderScriptStageTypeDelay, +} WifiMarauderScriptStageType; + +typedef enum { + WifiMarauderScriptScanTypeAp = 0, + WifiMarauderScriptScanTypeStation = 1 +} WifiMarauderScriptScanType; + +typedef enum { + WifiMarauderScriptSelectTypeAp, + WifiMarauderScriptSelectTypeStation, + WifiMarauderScriptSelectTypeSsid +} WifiMarauderScriptSelectType; + +// Stages +typedef struct WifiMarauderScriptStage { + WifiMarauderScriptStageType type; + void* stage; + struct WifiMarauderScriptStage* next_stage; +} WifiMarauderScriptStage; + +typedef struct WifiMarauderScriptStageScan { + WifiMarauderScriptScanType type; + int channel; + int timeout; +} WifiMarauderScriptStageScan; + +typedef struct WifiMarauderScriptStageSelect { + WifiMarauderScriptSelectType type; + char* filter; + int* indexes; + int index_count; + // TODO: Implement a feature to not select the same items in the next iteration of the script + bool allow_repeat; +} WifiMarauderScriptStageSelect; + +typedef struct WifiMarauderScriptStageDeauth { + int timeout; +} WifiMarauderScriptStageDeauth; + +typedef struct WifiMarauderScriptStageProbe { + int timeout; +} WifiMarauderScriptStageProbe; + +typedef struct WifiMarauderScriptStageSniffRaw { + int timeout; +} WifiMarauderScriptStageSniffRaw; + +typedef struct WifiMarauderScriptStageSniffBeacon { + int timeout; +} WifiMarauderScriptStageSniffBeacon; + +typedef struct WifiMarauderScriptStageSniffDeauth { + int timeout; +} WifiMarauderScriptStageSniffDeauth; + +typedef struct WifiMarauderScriptStageSniffEsp { + int timeout; +} WifiMarauderScriptStageSniffEsp; + +typedef struct WifiMarauderScriptStageSniffPmkid { + bool force_deauth; + bool hop_channels; + int channel; + int timeout; +} WifiMarauderScriptStageSniffPmkid; + +typedef struct WifiMarauderScriptStageSniffPwn { + int timeout; +} WifiMarauderScriptStageSniffPwn; + +typedef struct WifiMarauderScriptStageBeaconList { + char** ssids; + int ssid_count; + int random_ssids; + int timeout; +} WifiMarauderScriptStageBeaconList; + +typedef struct WifiMarauderScriptStageBeaconAp { + int timeout; +} WifiMarauderScriptStageBeaconAp; + +typedef struct WifiMarauderScriptStageExec { + char* command; +} WifiMarauderScriptStageExec; + +typedef struct WifiMarauderScriptStageDelay { + int timeout; +} WifiMarauderScriptStageDelay; + +// Script +typedef struct WifiMarauderScript { + char* name; + char* description; + WifiMarauderScriptStage* first_stage; + WifiMarauderScriptStage* last_stage; + WifiMarauderScriptBoolean enable_led; + WifiMarauderScriptBoolean save_pcap; + int repeat; +} WifiMarauderScript; + +typedef struct WifiMarauderScriptStageListItem { + char* value; + struct WifiMarauderScriptStageListItem* next_item; +} WifiMarauderScriptStageListItem; + +WifiMarauderScript* wifi_marauder_script_alloc(); +WifiMarauderScript* wifi_marauder_script_create(const char* script_name); +WifiMarauderScript* wifi_marauder_script_parse_raw(const char* script_raw); +WifiMarauderScript* wifi_marauder_script_parse_json(Storage* storage, const char* file_path); +void wifi_marauder_script_save_json( + Storage* storage, + const char* file_path, + WifiMarauderScript* script); +void wifi_marauder_script_add_stage( + WifiMarauderScript* script, + WifiMarauderScriptStageType stage_type, + void* stage_data); +bool wifi_marauder_script_has_stage( + WifiMarauderScript* script, + WifiMarauderScriptStageType stage_type); +void wifi_marauder_script_free(WifiMarauderScript* script); diff --git a/applications/external/esp32cam_marauder_companion/script/wifi_marauder_script_executor.c b/applications/external/esp32cam_marauder_companion/script/wifi_marauder_script_executor.c new file mode 100644 index 000000000..41f6285d4 --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/wifi_marauder_script_executor.c @@ -0,0 +1,324 @@ +#include "../wifi_marauder_app_i.h" +#include "wifi_marauder_script_executor.h" + +void _wifi_marauder_script_delay(WifiMarauderScriptWorker* worker, uint32_t delay_secs) { + for(uint32_t i = 0; i < delay_secs && worker->is_running; i++) furi_delay_ms(1000); +} + +void _send_stop() { + const char stop_command[] = "stopscan\n"; + wifi_marauder_uart_tx((uint8_t*)(stop_command), strlen(stop_command)); +} + +void _send_line_break() { + wifi_marauder_uart_tx((uint8_t*)("\n"), 1); +} + +void _send_channel_select(int channel) { + char command[30]; + _send_line_break(); + snprintf(command, sizeof(command), "channel -s %d\n", channel); + wifi_marauder_uart_tx((uint8_t*)(command), strlen(command)); +} + +void _wifi_marauder_script_execute_scan( + WifiMarauderScriptStageScan* stage, + WifiMarauderScriptWorker* worker) { + char command[15]; + // Set channel + if(stage->channel > 0) { + _send_channel_select(stage->channel); + } + // Start scan + if(stage->type == WifiMarauderScriptScanTypeAp) { + snprintf(command, sizeof(command), "scanap\n"); + } else { + snprintf(command, sizeof(command), "scansta\n"); + } + wifi_marauder_uart_tx((uint8_t*)(command), strlen(command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_select(WifiMarauderScriptStageSelect* stage) { + const char* select_type = NULL; + switch(stage->type) { + case WifiMarauderScriptSelectTypeAp: + select_type = "-a"; + break; + case WifiMarauderScriptSelectTypeStation: + select_type = "-c"; + break; + case WifiMarauderScriptSelectTypeSsid: + select_type = "-s"; + break; + default: + return; // invalid stage + } + + char command[256]; + size_t command_length = 0; + + if(stage->indexes != NULL && stage->index_count > 0) { + command_length = snprintf(command, sizeof(command), "select %s ", select_type); + + for(int i = 0; i < stage->index_count; i++) { + int index = stage->indexes[i]; + command_length += snprintf( + command + command_length, sizeof(command) - command_length, "%d, ", index); + } + + // Remove the trailing comma and space + command_length -= 2; + command[command_length] = '\n'; + command_length++; + } else if(stage->filter == NULL || strcmp(stage->filter, "all") == 0) { + command_length = snprintf(command, sizeof(command), "select %s all\n", select_type); + } else { + command_length = snprintf( + command, sizeof(command), "select %s -f \"%s\"\n", select_type, stage->filter); + } + + wifi_marauder_uart_tx((uint8_t*)command, command_length); +} + +void _wifi_marauder_script_execute_deauth( + WifiMarauderScriptStageDeauth* stage, + WifiMarauderScriptWorker* worker) { + const char attack_command[] = "attack -t deauth\n"; + wifi_marauder_uart_tx((uint8_t*)(attack_command), strlen(attack_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_probe( + WifiMarauderScriptStageProbe* stage, + WifiMarauderScriptWorker* worker) { + const char attack_command[] = "attack -t probe\n"; + wifi_marauder_uart_tx((uint8_t*)(attack_command), strlen(attack_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_sniff_raw( + WifiMarauderScriptStageSniffRaw* stage, + WifiMarauderScriptWorker* worker) { + const char sniff_command[] = "sniffraw\n"; + wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_sniff_beacon( + WifiMarauderScriptStageSniffBeacon* stage, + WifiMarauderScriptWorker* worker) { + const char sniff_command[] = "sniffbeacon\n"; + wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_sniff_deauth( + WifiMarauderScriptStageSniffDeauth* stage, + WifiMarauderScriptWorker* worker) { + const char sniff_command[] = "sniffdeauth\n"; + wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_sniff_esp( + WifiMarauderScriptStageSniffEsp* stage, + WifiMarauderScriptWorker* worker) { + const char sniff_command[] = "sniffesp\n"; + wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_sniff_pmkid( + WifiMarauderScriptStageSniffPmkid* stage, + WifiMarauderScriptWorker* worker) { + // If channel hopping is enabled, loop through channels 1-11 + if(stage->hop_channels) { + for(int i = 1; i <= 11; i++) { + char attack_command[50] = "sniffpmkid"; + int len = strlen(attack_command); + + len += snprintf(attack_command + len, sizeof(attack_command) - len, " -c %d", i); + if(stage->force_deauth) { + len += snprintf(attack_command + len, sizeof(attack_command) - len, " -d"); + } + + len += snprintf(attack_command + len, sizeof(attack_command) - len, "\n"); + wifi_marauder_uart_tx((uint8_t*)attack_command, len); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); + } + } else { + char attack_command[50] = "sniffpmkid"; + int len = strlen(attack_command); + + if(stage->channel > 0) { + len += snprintf( + attack_command + len, sizeof(attack_command) - len, " -c %d", stage->channel); + } + + if(stage->force_deauth) { + len += snprintf(attack_command + len, sizeof(attack_command) - len, " -d"); + } + len += snprintf(attack_command + len, sizeof(attack_command) - len, "\n"); + wifi_marauder_uart_tx((uint8_t*)attack_command, len); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); + } +} + +void _wifi_marauder_script_execute_sniff_pwn( + WifiMarauderScriptStageSniffPwn* stage, + WifiMarauderScriptWorker* worker) { + const char sniff_command[] = "sniffpwn\n"; + wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_beacon_list( + WifiMarauderScriptStageBeaconList* stage, + WifiMarauderScriptWorker* worker) { + const char clearlist_command[] = "clearlist -s\n"; + wifi_marauder_uart_tx((uint8_t*)(clearlist_command), strlen(clearlist_command)); + + char command[100]; + char* ssid; + + for(int i = 0; i < stage->ssid_count; i++) { + ssid = stage->ssids[i]; + snprintf(command, sizeof(command), "ssid -a -n \"%s\"", ssid); + wifi_marauder_uart_tx((uint8_t*)(command), strlen(command)); + _send_line_break(); + } + if(stage->random_ssids > 0) { + char add_random_command[50]; + snprintf( + add_random_command, + sizeof(add_random_command), + "ssid -a -r -g %d\n", + stage->random_ssids); + wifi_marauder_uart_tx((uint8_t*)add_random_command, strlen(add_random_command)); + } + const char attack_command[] = "attack -t beacon -l\n"; + wifi_marauder_uart_tx((uint8_t*)(attack_command), strlen(attack_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_beacon_ap( + WifiMarauderScriptStageBeaconAp* stage, + WifiMarauderScriptWorker* worker) { + const char command[] = "attack -t beacon -a\n"; + wifi_marauder_uart_tx((uint8_t*)command, strlen(command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_exec(WifiMarauderScriptStageExec* stage) { + if(stage->command != NULL) { + wifi_marauder_uart_tx((uint8_t*)stage->command, strlen(stage->command)); + _send_line_break(); + } +} + +void _wifi_marauder_script_execute_delay( + WifiMarauderScriptStageDelay* stage, + WifiMarauderScriptWorker* worker) { + _wifi_marauder_script_delay(worker, stage->timeout); +} + +void wifi_marauder_script_execute_start(void* context) { + furi_assert(context); + WifiMarauderScriptWorker* worker = context; + WifiMarauderScript* script = worker->script; + char command[100]; + + // Enables or disables the LED according to script settings + if(script->enable_led != WifiMarauderScriptBooleanUndefined) { + snprintf( + command, + sizeof(command), + "settings -s EnableLED %s", + script->enable_led ? "enable" : "disable"); + wifi_marauder_uart_tx((uint8_t*)command, strlen(command)); + _send_line_break(); + } + + // Enables or disables PCAP saving according to script settings + if(script->save_pcap != WifiMarauderScriptBooleanUndefined) { + snprintf( + command, + sizeof(command), + "settings -s SavePCAP %s", + script->save_pcap ? "enable" : "disable"); + wifi_marauder_uart_tx((uint8_t*)command, strlen(command)); + _send_line_break(); + } +} + +void wifi_marauder_script_execute_stage(WifiMarauderScriptStage* stage, void* context) { + furi_assert(context); + WifiMarauderScriptWorker* worker = context; + void* stage_data = stage->stage; + + switch(stage->type) { + case WifiMarauderScriptStageTypeScan: + _wifi_marauder_script_execute_scan((WifiMarauderScriptStageScan*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSelect: + _wifi_marauder_script_execute_select((WifiMarauderScriptStageSelect*)stage_data); + break; + case WifiMarauderScriptStageTypeDeauth: + _wifi_marauder_script_execute_deauth((WifiMarauderScriptStageDeauth*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeProbe: + _wifi_marauder_script_execute_probe((WifiMarauderScriptStageProbe*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffRaw: + _wifi_marauder_script_execute_sniff_raw( + (WifiMarauderScriptStageSniffRaw*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffBeacon: + _wifi_marauder_script_execute_sniff_beacon( + (WifiMarauderScriptStageSniffBeacon*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffDeauth: + _wifi_marauder_script_execute_sniff_deauth( + (WifiMarauderScriptStageSniffDeauth*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffEsp: + _wifi_marauder_script_execute_sniff_esp( + (WifiMarauderScriptStageSniffEsp*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffPmkid: + _wifi_marauder_script_execute_sniff_pmkid( + (WifiMarauderScriptStageSniffPmkid*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffPwn: + _wifi_marauder_script_execute_sniff_pwn( + (WifiMarauderScriptStageSniffPwn*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeBeaconList: + _wifi_marauder_script_execute_beacon_list( + (WifiMarauderScriptStageBeaconList*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeBeaconAp: + _wifi_marauder_script_execute_beacon_ap( + (WifiMarauderScriptStageBeaconAp*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeExec: + _wifi_marauder_script_execute_exec((WifiMarauderScriptStageExec*)stage_data); + break; + case WifiMarauderScriptStageTypeDelay: + _wifi_marauder_script_execute_delay((WifiMarauderScriptStageDelay*)stage_data, worker); + break; + } +} \ No newline at end of file diff --git a/applications/external/esp32cam_marauder_companion/script/wifi_marauder_script_executor.h b/applications/external/esp32cam_marauder_companion/script/wifi_marauder_script_executor.h new file mode 100644 index 000000000..654712849 --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/wifi_marauder_script_executor.h @@ -0,0 +1,6 @@ +#pragma once + +#include "wifi_marauder_script.h" + +void wifi_marauder_script_execute_start(void* context); +void wifi_marauder_script_execute_stage(WifiMarauderScriptStage* stage, void* context); diff --git a/applications/external/esp32cam_marauder_companion/script/wifi_marauder_script_worker.c b/applications/external/esp32cam_marauder_companion/script/wifi_marauder_script_worker.c new file mode 100644 index 000000000..2e11b0e5f --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/wifi_marauder_script_worker.c @@ -0,0 +1,75 @@ +#include "../wifi_marauder_app_i.h" +#include "wifi_marauder_script_worker.h" + +WifiMarauderScriptWorker* wifi_marauder_script_worker_alloc() { + WifiMarauderScriptWorker* worker = malloc(sizeof(WifiMarauderScriptWorker)); + if(worker == NULL) { + return NULL; + } + worker->callback_start = NULL; + worker->callback_stage = NULL; + worker->worker_thread = NULL; + worker->is_running = false; + return worker; +} + +int32_t _wifi_marauder_script_worker_task(void* worker) { + WifiMarauderScriptWorker* script_worker = worker; + WifiMarauderScript* script = script_worker->script; + if(script == NULL) { + return WifiMarauderScriptWorkerStatusInvalidScript; + } + + // Setup + script_worker->callback_start(script_worker->context); + if(!script_worker->is_running) { + return WifiMarauderScriptWorkerStatusForceExit; + } + + // Stages + for(int i = 0; i < script->repeat; i++) { + WifiMarauderScriptStage* current_stage = script->first_stage; + while(current_stage != NULL && script_worker->is_running) { + script_worker->callback_stage(current_stage, script_worker->context); + current_stage = current_stage->next_stage; + } + if(!script_worker->is_running) { + return WifiMarauderScriptWorkerStatusForceExit; + } + } + + script_worker->is_running = false; + + return WifiMarauderScriptWorkerStatusSuccess; +} + +bool wifi_marauder_script_worker_start( + WifiMarauderScriptWorker* instance, + WifiMarauderScript* script) { + if(!instance || !script) { + return false; + } + instance->callback_start = wifi_marauder_script_execute_start; + instance->callback_stage = wifi_marauder_script_execute_stage; + instance->script = script; + instance->context = instance; + instance->is_running = true; + instance->worker_thread = furi_thread_alloc_ex( + "WifiMarauderScriptWorker", 1024, _wifi_marauder_script_worker_task, instance); + if(!instance->worker_thread) { + return false; + } + furi_thread_start(instance->worker_thread); + return true; +} + +void wifi_marauder_script_worker_free(WifiMarauderScriptWorker* worker) { + if(worker != NULL) { + if(worker->worker_thread != NULL) { + worker->is_running = false; + furi_thread_join(worker->worker_thread); + furi_thread_free(worker->worker_thread); + } + free(worker); + } +} \ No newline at end of file diff --git a/applications/external/esp32cam_marauder_companion/script/wifi_marauder_script_worker.h b/applications/external/esp32cam_marauder_companion/script/wifi_marauder_script_worker.h new file mode 100644 index 000000000..76ff070d2 --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/script/wifi_marauder_script_worker.h @@ -0,0 +1,43 @@ +#pragma once + +#include "wifi_marauder_script.h" + +typedef enum { + WifiMarauderScriptWorkerStatusSuccess = 0, + WifiMarauderScriptWorkerStatusInvalidScript = 1, + WifiMarauderScriptWorkerStatusForceExit = 2 +} WifiMarauderScriptWorkerStatus; + +typedef struct WifiMarauderScriptWorker { + WifiMarauderScript* script; + FuriThread* worker_thread; + void (*callback_start)(void*); + void (*callback_stage)(WifiMarauderScriptStage*, void*); + void* context; + bool is_running; +} WifiMarauderScriptWorker; + +/** + * @brief Allocates a new instance of WifiMarauderScriptWorker. + * + * @return A pointer to the allocated instance or NULL if allocation fails. + */ +WifiMarauderScriptWorker* wifi_marauder_script_worker_alloc(); + +/** + * @brief Starts the execution of the worker and sets the callback function to be called after each stage is executed. + * + * @param instance A pointer to the instance of WifiMarauderScriptWorker to start. + * @param script Script to be executed + * @return True if the worker was successfully started, false otherwise. + */ +bool wifi_marauder_script_worker_start( + WifiMarauderScriptWorker* instance, + WifiMarauderScript* script); + +/** + * @brief Frees the memory used by the instance of WifiMarauderScriptWorker. + * + * @param script A pointer to the instance of WifiMarauderScriptWorker to free. + */ +void wifi_marauder_script_worker_free(WifiMarauderScriptWorker* script); diff --git a/applications/external/esp32cam_marauder_companion/wifi_marauder_app.c b/applications/external/esp32cam_marauder_companion/wifi_marauder_app.c index 0f3d4e00a..2065b057c 100644 --- a/applications/external/esp32cam_marauder_companion/wifi_marauder_app.c +++ b/applications/external/esp32cam_marauder_companion/wifi_marauder_app.c @@ -79,6 +79,11 @@ WifiMarauderApp* wifi_marauder_app_alloc() { (!storage_file_exists(app->storage, SAVE_PCAP_SETTING_FILEPATH) || !storage_file_exists(app->storage, SAVE_LOGS_SETTING_FILEPATH)); + // Submenu + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, WifiMarauderAppViewSubmenu, submenu_get_view(app->submenu)); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneStart); return app; @@ -98,6 +103,10 @@ void wifi_marauder_make_app_folder(WifiMarauderApp* app) { if(!storage_simply_mkdir(app->storage, MARAUDER_APP_FOLDER_LOGS)) { dialog_message_show_storage_error(app->dialogs, "Cannot create\npcaps folder"); } + + if(!storage_simply_mkdir(app->storage, MARAUDER_APP_FOLDER_SCRIPTS)) { + dialog_message_show_storage_error(app->dialogs, "Cannot create\nscripts folder"); + } } void wifi_marauder_load_settings(WifiMarauderApp* app) { @@ -132,10 +141,14 @@ void wifi_marauder_app_free(WifiMarauderApp* app) { view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewConsoleOutput); view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewTextInput); view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewWidget); + view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); + widget_free(app->widget); text_box_free(app->text_box); furi_string_free(app->text_box_store); text_input_free(app->text_input); + submenu_free(app->submenu); + variable_item_list_free(app->var_item_list); storage_file_free(app->capture_file); storage_file_free(app->log_file); storage_file_free(app->save_pcap_setting_file); diff --git a/applications/external/esp32cam_marauder_companion/wifi_marauder_app.h b/applications/external/esp32cam_marauder_companion/wifi_marauder_app.h index 89a06f904..785c80334 100644 --- a/applications/external/esp32cam_marauder_companion/wifi_marauder_app.h +++ b/applications/external/esp32cam_marauder_companion/wifi_marauder_app.h @@ -4,7 +4,7 @@ extern "C" { #endif -#define WIFI_MARAUDER_APP_VERSION "v0.3.3" +#define WIFI_MARAUDER_APP_VERSION "v0.6.2" typedef struct WifiMarauderApp WifiMarauderApp; diff --git a/applications/external/esp32cam_marauder_companion/wifi_marauder_app_i.h b/applications/external/esp32cam_marauder_companion/wifi_marauder_app_i.h index bd1fb3071..463ab4c46 100644 --- a/applications/external/esp32cam_marauder_companion/wifi_marauder_app_i.h +++ b/applications/external/esp32cam_marauder_companion/wifi_marauder_app_i.h @@ -6,20 +6,28 @@ #include "scenes/wifi_marauder_scene.h" #include "wifi_marauder_custom_event.h" #include "wifi_marauder_uart.h" -#include "wifi_marauder_pcap.h" +#include "file/sequential_file.h" +#include "script/wifi_marauder_script.h" +#include "script/wifi_marauder_script_worker.h" +#include "script/wifi_marauder_script_executor.h" +#include "script/menu/wifi_marauder_script_stage_menu.h" #include #include #include #include -#include +#include #include #include +#include +#include "mayhem_marauder_icons.h" +#include #include +#include #include -#define NUM_MENU_ITEMS (18) +#define NUM_MENU_ITEMS (20) #define WIFI_MARAUDER_TEXT_BOX_STORE_SIZE (4096) #define WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE (512) @@ -30,9 +38,17 @@ #define MARAUDER_APP_FOLDER_LOGS MARAUDER_APP_FOLDER "/logs" #define MARAUDER_APP_FOLDER_USER_PCAPS MARAUDER_APP_FOLDER_USER "/pcaps" #define MARAUDER_APP_FOLDER_USER_LOGS MARAUDER_APP_FOLDER_USER "/logs" +#define MARAUDER_APP_FOLDER_SCRIPTS MARAUDER_APP_FOLDER "/scripts" +#define MARAUDER_APP_SCRIPT_PATH(file_name) MARAUDER_APP_FOLDER_SCRIPTS "/" file_name ".json" #define SAVE_PCAP_SETTING_FILEPATH MARAUDER_APP_FOLDER "/save_pcaps_here.setting" #define SAVE_LOGS_SETTING_FILEPATH MARAUDER_APP_FOLDER "/save_logs_here.setting" +typedef enum WifiMarauderUserInputType { + WifiMarauderUserInputTypeString, + WifiMarauderUserInputTypeNumber, + WifiMarauderUserInputTypeFileName +} WifiMarauderUserInputType; + struct WifiMarauderApp { Gui* gui; ViewDispatcher* view_dispatcher; @@ -58,6 +74,7 @@ struct WifiMarauderApp { VariableItemList* var_item_list; Widget* widget; + Submenu* submenu; int open_log_file_page; int open_log_file_num_pages; @@ -73,6 +90,26 @@ struct WifiMarauderApp { bool is_writing_pcap; bool is_writing_log; + // User input + WifiMarauderUserInputType user_input_type; + char** user_input_string_reference; + int* user_input_number_reference; + char* user_input_file_dir; + char* user_input_file_extension; + + // Automation script + WifiMarauderScript* script; + WifiMarauderScriptWorker* script_worker; + FuriString** script_list; + int script_list_count; + WifiMarauderScriptStage* script_edit_selected_stage; + WifiMarauderScriptStageMenu* script_stage_menu; + WifiMarauderScriptStageListItem* script_stage_edit_first_item; + char*** script_stage_edit_strings_reference; + int* script_stage_edit_string_count_reference; + int** script_stage_edit_numbers_reference; + int* script_stage_edit_number_count_reference; + // For input source and destination MAC in targeted deauth attack int special_case_input_step; char special_case_input_src_addr[20]; @@ -105,4 +142,5 @@ typedef enum { WifiMarauderAppViewConsoleOutput, WifiMarauderAppViewTextInput, WifiMarauderAppViewWidget, + WifiMarauderAppViewSubmenu, } WifiMarauderAppView; diff --git a/applications/external/esp32cam_marauder_companion/wifi_marauder_custom_event.h b/applications/external/esp32cam_marauder_companion/wifi_marauder_custom_event.h index 79f96b107..b79632e0c 100644 --- a/applications/external/esp32cam_marauder_companion/wifi_marauder_custom_event.h +++ b/applications/external/esp32cam_marauder_companion/wifi_marauder_custom_event.h @@ -7,5 +7,7 @@ typedef enum { WifiMarauderEventSaveSourceMac, WifiMarauderEventSaveDestinationMac, WifiMarauderEventStartSettingsInit, - WifiMarauderEventStartLogViewer + WifiMarauderEventStartLogViewer, + WifiMarauderEventStartScriptSelect, + WifiMarauderEventStartSniffPmkidOptions, } WifiMarauderCustomEvent; diff --git a/applications/external/esp32cam_marauder_companion/wifi_marauder_pcap.c b/applications/external/esp32cam_marauder_companion/wifi_marauder_pcap.c deleted file mode 100644 index 73e3d98ea..000000000 --- a/applications/external/esp32cam_marauder_companion/wifi_marauder_pcap.c +++ /dev/null @@ -1,64 +0,0 @@ -#include "wifi_marauder_app_i.h" -#include "wifi_marauder_pcap.h" - -void wifi_marauder_get_prefix_from_sniff_cmd(char* dest, const char* command) { - int start, end, delta; - start = strlen("sniff"); - end = strcspn(command, " "); - delta = end - start; - strncpy(dest, command + start, end - start); - dest[delta] = '\0'; -} - -void wifi_marauder_get_prefix_from_cmd(char* dest, const char* command) { - int end; - end = strcspn(command, " "); - strncpy(dest, command, end); - dest[end] = '\0'; -} - -void wifi_marauder_create_pcap_file(WifiMarauderApp* app) { - char prefix[10]; - char capture_file_path[100]; - wifi_marauder_get_prefix_from_sniff_cmd(prefix, app->selected_tx_string); - - int i = 0; - do { - snprintf( - capture_file_path, - sizeof(capture_file_path), - "%s/%s_%d.pcap", - MARAUDER_APP_FOLDER_PCAPS, - prefix, - i); - i++; - } while(storage_file_exists(app->storage, capture_file_path)); - - if(!storage_file_open(app->capture_file, capture_file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { - dialog_message_show_storage_error(app->dialogs, "Cannot open pcap file"); - } -} - -void wifi_marauder_create_log_file(WifiMarauderApp* app) { - char prefix[10]; - char log_file_path[100]; - wifi_marauder_get_prefix_from_cmd(prefix, app->selected_tx_string); - - int i = 0; - do { - snprintf( - log_file_path, - sizeof(log_file_path), - "%s/%s_%d.log", - MARAUDER_APP_FOLDER_LOGS, - prefix, - i); - i++; - } while(storage_file_exists(app->storage, log_file_path)); - - if(!storage_file_open(app->log_file, log_file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { - dialog_message_show_storage_error(app->dialogs, "Cannot open log file"); - } else { - strcpy(app->log_file_path, log_file_path); - } -} \ No newline at end of file diff --git a/applications/external/esp32cam_marauder_companion/wifi_marauder_pcap.h b/applications/external/esp32cam_marauder_companion/wifi_marauder_pcap.h deleted file mode 100644 index 94f6282f3..000000000 --- a/applications/external/esp32cam_marauder_companion/wifi_marauder_pcap.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "furi_hal.h" - -/** - * Creates a PCAP file to store incoming packets. - * The file name will have a prefix according to the type of scan being performed by the application (Eg: raw_0.pcap) - * - * @param app Application context - */ -void wifi_marauder_create_pcap_file(WifiMarauderApp* app); - -/** - * Creates a log file to store text from console output. - * The file name will have a prefix according to the command being performed by the application (Eg: scanap_0.log) - * - * @param app Application context - */ -// same as wifi_marauder_create_pcap_file, but for log files (to save console text output) -void wifi_marauder_create_log_file(WifiMarauderApp* app); diff --git a/applications/external/esp32cam_marauder_companion/wifi_marauder_validators.c b/applications/external/esp32cam_marauder_companion/wifi_marauder_validators.c new file mode 100644 index 000000000..5bec88269 --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/wifi_marauder_validators.c @@ -0,0 +1,57 @@ +#include +#include "wifi_marauder_validators.h" +#include + +struct ValidatorIsFile { + char* app_path_folder; + const char* app_extension; + char* current_name; +}; + +bool validator_is_file_callback(const char* text, FuriString* error, void* context) { + furi_assert(context); + ValidatorIsFile* instance = context; + + if(instance->current_name != NULL) { + if(strcmp(instance->current_name, text) == 0) { + return true; + } + } + + bool ret = true; + FuriString* path = furi_string_alloc_printf( + "%s/%s%s", instance->app_path_folder, text, instance->app_extension); + Storage* storage = furi_record_open(RECORD_STORAGE); + if(storage_common_stat(storage, furi_string_get_cstr(path), NULL) == FSE_OK) { + ret = false; + furi_string_printf(error, "This name\nexists!\nChoose\nanother one."); + } else { + ret = true; + } + furi_string_free(path); + furi_record_close(RECORD_STORAGE); + + return ret; +} + +ValidatorIsFile* validator_is_file_alloc_init( + const char* app_path_folder, + const char* app_extension, + const char* current_name) { + ValidatorIsFile* instance = malloc(sizeof(ValidatorIsFile)); + + instance->app_path_folder = strdup(app_path_folder); + instance->app_extension = app_extension; + if(current_name != NULL) { + instance->current_name = strdup(current_name); + } + + return instance; +} + +void validator_is_file_free(ValidatorIsFile* instance) { + furi_assert(instance); + free(instance->app_path_folder); + free(instance->current_name); + free(instance); +} diff --git a/applications/external/esp32cam_marauder_companion/wifi_marauder_validators.h b/applications/external/esp32cam_marauder_companion/wifi_marauder_validators.h new file mode 100644 index 000000000..d9200b6db --- /dev/null +++ b/applications/external/esp32cam_marauder_companion/wifi_marauder_validators.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif +typedef struct ValidatorIsFile ValidatorIsFile; + +ValidatorIsFile* validator_is_file_alloc_init( + const char* app_path_folder, + const char* app_extension, + const char* current_name); + +void validator_is_file_free(ValidatorIsFile* instance); + +bool validator_is_file_callback(const char* text, FuriString* error, void* context); + +#ifdef __cplusplus +} +#endif