diff options
author | Albert Kharisov <ah@bright-box.com> | 2021-08-19 03:18:42 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-19 03:18:42 +0300 |
commit | 5f6aff22558db8c43a5a8f51ccbf2b033c553d47 (patch) | |
tree | 29465c046b6a470ca3b858061601607cfec524ea /applications/irda | |
parent | 9d38f28de72dae99034d5770d56fb12a87d2a6f6 (diff) |
[FL-1472, FL-1596, FL-1673] IRDA: stability improvements (#655)
- Restrict with 31 bytes length for remote and signal name
- Don't stuck for 0 PWM cycle timings
- Support timings > 65535 PWM cycles
- Fix remote file open error
- Add IRDA TX debug redirect
- Add remote parse error print, improve parsing, support tabs
- Fix stucks with uncorrect RAW signal values, long strings in remote file, etc
- Fix HAL signals capturing (save previous read value)
- Fix leak in case of failed parsing
Diffstat (limited to 'applications/irda')
-rw-r--r-- | applications/irda/cli/irda-cli.cpp | 30 | ||||
-rw-r--r-- | applications/irda/irda-app-brute-force.cpp | 14 | ||||
-rw-r--r-- | applications/irda/irda-app-brute-force.h | 5 | ||||
-rw-r--r-- | applications/irda/irda-app-file-parser.cpp | 151 | ||||
-rw-r--r-- | applications/irda/irda-app-file-parser.h | 15 | ||||
-rw-r--r-- | applications/irda/irda-app-remote-manager.cpp | 17 | ||||
-rw-r--r-- | applications/irda/irda-app-remote-manager.h | 16 | ||||
-rw-r--r-- | applications/irda/irda-app-signal.cpp | 2 | ||||
-rw-r--r-- | applications/irda/irda-app.cpp | 12 | ||||
-rw-r--r-- | applications/irda/irda-app.h | 15 | ||||
-rw-r--r-- | applications/irda/scene/irda-app-scene-edit-rename.cpp | 13 | ||||
-rw-r--r-- | applications/irda/scene/irda-app-scene-learn-enter-name.cpp | 2 |
12 files changed, 205 insertions, 87 deletions
diff --git a/applications/irda/cli/irda-cli.cpp b/applications/irda/cli/irda-cli.cpp index 6cde9c95..1becdfbb 100644 --- a/applications/irda/cli/irda-cli.cpp +++ b/applications/irda/cli/irda-cli.cpp @@ -74,6 +74,12 @@ static void irda_cli_print_usage(void) { printf(" %s", irda_get_protocol_name((IrdaProtocol)i)); } printf("\r\n"); + printf("\tRaw format:\r\n"); + printf("\tir_tx RAW F:<frequency> DC:<duty_cycle> <sample0> <sample1>...\r\n"); + printf( + "\tFrequency (%d - %d), Duty cycle (0 - 100), max 512 samples\r\n", + IRDA_MIN_FREQUENCY, + IRDA_MAX_FREQUENCY); } static bool parse_message(const char* str, IrdaMessage* message) { @@ -115,6 +121,14 @@ static bool parse_signal_raw( *duty_cycle = (float)atoi(duty_cycle_str) / 100; str += strlen(frequency_str) + strlen(duty_cycle_str) + 10; + if((*frequency > IRDA_MAX_FREQUENCY) || (*frequency < IRDA_MIN_FREQUENCY)) { + return false; + } + + if((*duty_cycle <= 0) || (*duty_cycle > 1)) { + return false; + } + uint32_t timings_cnt_max = *timings_cnt; *timings_cnt = 0; @@ -131,13 +145,15 @@ static bool parse_signal_raw( ++*timings_cnt; } - printf("\r\nTransmit:"); - for(size_t i = 0; i < *timings_cnt; ++i) { - printf(" %ld", timings[i]); + if(*timings_cnt > 0) { + printf("\r\nTransmit:"); + for(size_t i = 0; i < *timings_cnt; ++i) { + printf(" %ld", timings[i]); + } + printf("\r\n"); } - printf("\r\n"); - return true; + return (parsed == 2) && (*timings_cnt > 0); } static void irda_cli_start_ir_tx(Cli* cli, string_t args, void* context) { @@ -150,8 +166,8 @@ static void irda_cli_start_ir_tx(Cli* cli, string_t args, void* context) { const char* str = string_get_cstr(args); uint32_t frequency; float duty_cycle; - uint32_t* timings = (uint32_t*)furi_alloc(sizeof(uint32_t) * 1000); - uint32_t timings_cnt = 1000; + uint32_t* timings = (uint32_t*)furi_alloc(sizeof(uint32_t) * 512); + uint32_t timings_cnt = 512; if(parse_message(str, &message)) { irda_send(&message, 1); diff --git a/applications/irda/irda-app-brute-force.cpp b/applications/irda/irda-app-brute-force.cpp index 42797d21..ab8c039b 100644 --- a/applications/irda/irda-app-brute-force.cpp +++ b/applications/irda/irda-app-brute-force.cpp @@ -1,8 +1,10 @@ #include "irda-app-brute-force.h" #include "irda/irda-app-file-parser.h" -#include "m-string.h" -#include <file-worker-cpp.h> + #include <memory> +#include <m-string.h> +#include <furi.h> +#include <file-worker-cpp.h> void IrdaAppBruteForce::add_record(int index, const char* name) { records[name].index = index; @@ -16,7 +18,7 @@ bool IrdaAppBruteForce::calculate_messages() { file_parser = std::make_unique<IrdaAppFileParser>(); fs_res = file_parser->open_irda_file_read(universal_db_filename); if(!fs_res) { - file_parser.reset(nullptr); + file_parser.reset(); return false; } @@ -31,7 +33,7 @@ bool IrdaAppBruteForce::calculate_messages() { } file_parser->close(); - file_parser.reset(nullptr); + file_parser.reset(); return true; } @@ -43,7 +45,7 @@ void IrdaAppBruteForce::stop_bruteforce() { furi_assert(file_parser); current_record.clear(); file_parser->close(); - file_parser.reset(nullptr); + file_parser.reset(); } } @@ -81,7 +83,7 @@ bool IrdaAppBruteForce::start_bruteforce(int index, int& record_amount) { file_parser = std::make_unique<IrdaAppFileParser>(); result = file_parser->open_irda_file_read(universal_db_filename); if(!result) { - (void)file_parser.reset(nullptr); + file_parser.reset(); } } diff --git a/applications/irda/irda-app-brute-force.h b/applications/irda/irda-app-brute-force.h index 174f74f5..58736759 100644 --- a/applications/irda/irda-app-brute-force.h +++ b/applications/irda/irda-app-brute-force.h @@ -1,7 +1,8 @@ #pragma once -#include "furi/check.h" -#include <unordered_map> + #include "irda-app-file-parser.h" + +#include <unordered_map> #include <memory> class IrdaAppBruteForce { diff --git a/applications/irda/irda-app-file-parser.cpp b/applications/irda/irda-app-file-parser.cpp index 8cd0499b..f462919e 100644 --- a/applications/irda/irda-app-file-parser.cpp +++ b/applications/irda/irda-app-file-parser.cpp @@ -1,22 +1,16 @@ #include "irda-app-file-parser.h" -#include "furi/check.h" #include "irda-app-remote-manager.h" #include "irda-app-signal.h" -#include "m-string.h" + +#include <m-string.h> +#include <cstdio> #include <text-store.h> #include <irda.h> -#include <cstdio> -#include <stdint.h> -#include <string> #include <string_view> #include <furi.h> +#include <furi-hal-irda.h> #include <file-worker-cpp.h> -uint32_t const IrdaAppFileParser::max_line_length = ((9 + 1) * 512 + 100); -const char* IrdaAppFileParser::irda_directory = "/any/irda"; -const char* IrdaAppFileParser::irda_extension = ".ir"; -uint32_t const IrdaAppFileParser::max_raw_timings_in_signal = 512; - bool IrdaAppFileParser::open_irda_file_read(const char* name) { std::string full_filename; if(name[0] != '/') @@ -158,18 +152,41 @@ std::unique_ptr<IrdaAppFileParser::IrdaFileSignal> IrdaProtocol protocol = irda_get_protocol_by_name(protocol_name); if(!irda_is_protocol_valid((IrdaProtocol)protocol)) { + size_t end_of_str = MIN(str.find_last_not_of(" \t\r\n") + 1, (size_t)30); + FURI_LOG_E( + "IrdaFileParser", + "Unknown protocol(\'%.*s...\'): \'%s\'", + end_of_str, + str.c_str(), + protocol_name); return nullptr; } int address_length = irda_get_protocol_address_length(protocol); uint32_t address_mask = (1LU << (4 * address_length)) - 1; if(address != (address & address_mask)) { + size_t end_of_str = MIN(str.find_last_not_of(" \t\r\n") + 1, (size_t)30); + FURI_LOG_E( + "IrdaFileParser", + "Signal(\'%.*s...\'): address is too long (mask for this protocol is 0x%08X): 0x%X", + end_of_str, + str.c_str(), + address_mask, + address); return nullptr; } int command_length = irda_get_protocol_command_length(protocol); uint32_t command_mask = (1LU << (4 * command_length)) - 1; if(command != (command & command_mask)) { + size_t end_of_str = MIN(str.find_last_not_of(" \t\r\n") + 1, (size_t)30); + FURI_LOG_E( + "IrdaFileParser", + "Signal(\'%.*s...\'): command is too long (mask for this protocol is 0x%08X): 0x%X", + end_of_str, + str.c_str(), + command_mask, + command); return nullptr; } @@ -195,34 +212,81 @@ const char* find_first_not_of(const char* str, char symbol) { return str; } +static int remove_args32(std::string_view& str, size_t num) { + int removed_length = 0; + + while(num--) { + char buf[32]; + + size_t index = str.find_first_not_of(" \t"); + if(index == std::string_view::npos) break; + removed_length += index; + str.remove_prefix(index); + + if(str.empty()) break; + + int parsed = std::sscanf(str.data(), "%31s", buf); + if(!parsed) break; + + size_t len = strlen(buf); + if(!len) break; + removed_length += len; + str.remove_prefix(len); + + if(str.empty()) break; + } + + return removed_length; +} + std::unique_ptr<IrdaAppFileParser::IrdaFileSignal> IrdaAppFileParser::parse_signal_raw(const std::string& string) const { uint32_t frequency; uint32_t duty_cycle; - int str_len = string.size(); std::string_view str(string.c_str()); auto irda_file_signal = std::make_unique<IrdaFileSignal>(); int parsed = std::sscanf( str.data(), "%31s RAW F:%ld DC:%ld", irda_file_signal->name, &frequency, &duty_cycle); - if((parsed != 3) || (frequency > 42000) || (frequency < 32000) || (duty_cycle == 0) || - (duty_cycle >= 100)) { + if(parsed != 3) { return nullptr; } - char dummy[100] = {0}; - int header_len = 0; - header_len = sniprintf( - dummy, - sizeof(dummy), - "%.31s RAW F:%ld DC:%ld", - irda_file_signal->name, - frequency, - duty_cycle); + if((frequency < IRDA_MIN_FREQUENCY) || (frequency > IRDA_MAX_FREQUENCY)) { + size_t end_of_str = MIN(string.find_last_not_of(" \t\r\n") + 1, (size_t)30); + FURI_LOG_E( + "IrdaFileParser", + "RAW signal(\'%.*s...\'): frequency is out of bounds (%ld-%ld): %ld", + end_of_str, + string.c_str(), + IRDA_MIN_FREQUENCY, + IRDA_MAX_FREQUENCY, + frequency); + return nullptr; + } + + if((duty_cycle == 0) || (duty_cycle > 100)) { + size_t end_of_str = MIN(string.find_last_not_of(" \t\r\n") + 1, (size_t)30); + FURI_LOG_E( + "IrdaFileParser", + "RAW signal(\'%.*s...\'): duty cycle is out of bounds (0-100): %ld", + end_of_str, + string.c_str(), + duty_cycle); + return nullptr; + } - furi_assert(header_len < str_len); - str.remove_prefix(header_len); + int header_len = remove_args32(str, 4); + + size_t last_valid_ch = str.find_last_not_of(" \t\r\n"); + if(last_valid_ch != std::string_view::npos) { + str.remove_suffix(str.size() - last_valid_ch - 1); + } else { + FURI_LOG_E( + "IrdaFileParser", "RAW signal(\'%.*s\'): no timings", header_len, string.c_str()); + return nullptr; + } /* move allocated timings into raw signal object */ IrdaAppSignal::RawSignal raw_signal = { @@ -231,40 +295,59 @@ std::unique_ptr<IrdaAppFileParser::IrdaFileSignal> while(!str.empty()) { char buf[10]; - size_t index = str.find_first_not_of(' ', 1); + size_t index = str.find_first_not_of(" \t", 1); if(index == std::string_view::npos) { break; } str.remove_prefix(index); parsed = std::sscanf(str.data(), "%9s", buf); if(parsed != 1) { + FURI_LOG_E( + "IrdaFileParser", + "RAW signal(\'%.*s...\'): failed on timing[%ld] \'%*s\'", + header_len, + string.c_str(), + raw_signal.timings_cnt, + str.size(), + str.data()); result = false; - furi_assert(0); break; } str.remove_prefix(strlen(buf)); int value = atoi(buf); if(value <= 0) { + FURI_LOG_E( + "IrdaFileParser", + "RAW signal(\'%.*s...\'): failed on timing[%ld] \'%s\'", + header_len, + string.c_str(), + raw_signal.timings_cnt, + buf); result = false; - furi_assert(0); break; } - raw_signal.timings[raw_signal.timings_cnt] = value; - ++raw_signal.timings_cnt; - result = true; + if(raw_signal.timings_cnt >= max_raw_timings_in_signal) { + FURI_LOG_E( + "IrdaFileParser", + "RAW signal(\'%.*s...\'): too much timings (max %ld)", + header_len, + string.c_str(), + max_raw_timings_in_signal); result = false; - furi_assert(0); break; } + raw_signal.timings[raw_signal.timings_cnt] = value; + ++raw_signal.timings_cnt; + result = true; } if(result) { /* copy timings instead of moving them to occupy less than max_raw_timings_in_signal */ irda_file_signal->signal.copy_raw_signal(raw_signal.timings, raw_signal.timings_cnt); } else { - (void)irda_file_signal.release(); + irda_file_signal.reset(); } delete[] raw_signal.timings; return irda_file_signal; @@ -306,13 +389,11 @@ bool IrdaAppFileParser::check_errors() { } std::string IrdaAppFileParser::file_select(const char* selected) { - TextStore* filename_ts = new TextStore(128); + auto filename_ts = std::make_unique<TextStore>(IrdaAppRemoteManager::max_remote_name_length); bool result; result = file_worker.file_select( irda_directory, irda_extension, filename_ts->text, filename_ts->text_size, selected); - delete filename_ts; - return result ? std::string(filename_ts->text) : std::string(); } diff --git a/applications/irda/irda-app-file-parser.h b/applications/irda/irda-app-file-parser.h index f3d42e49..b0c98ad6 100644 --- a/applications/irda/irda-app-file-parser.h +++ b/applications/irda/irda-app-file-parser.h @@ -1,8 +1,12 @@ #pragma once + +#include "irda-app-signal.h" + #include <irda.h> #include <file-worker-cpp.h> -#include "irda-app-signal.h" #include <memory> +#include <string> +#include <cstdint> class IrdaAppFileParser { public: @@ -40,10 +44,11 @@ private: std::unique_ptr<IrdaFileSignal> parse_signal_raw(const std::string& str) const; std::string make_full_name(const std::string& name) const; - static const char* irda_directory; - static const char* irda_extension; - static const uint32_t max_line_length; - static uint32_t const max_raw_timings_in_signal; + static inline const char* const irda_directory = "/any/irda"; + static inline const char* const irda_extension = ".ir"; + static inline const uint32_t max_raw_timings_in_signal = 512; + static inline const uint32_t max_line_length = + (9 + 1) * IrdaAppFileParser::max_raw_timings_in_signal + 100; FileWorkerCpp file_worker; char file_buf[128]; diff --git a/applications/irda/irda-app-remote-manager.cpp b/applications/irda/irda-app-remote-manager.cpp index cd98893e..b3f713ce 100644 --- a/applications/irda/irda-app-remote-manager.cpp +++ b/applications/irda/irda-app-remote-manager.cpp @@ -1,15 +1,14 @@ #include "irda-app-remote-manager.h" -#include <storage/storage.h> -#include "furi.h" -#include "furi/check.h" -#include "gui/modules/button_menu.h" -#include "irda.h" -#include <cstdio> -#include <stdint.h> -#include <string> -#include <utility> #include "irda-app-file-parser.h" +#include <utility> + +#include <irda.h> +#include <cstdio> +#include <furi.h> +#include <gui/modules/button_menu.h> +#include <storage/storage.h> + static const std::string default_remote_name = "remote"; std::string IrdaAppRemoteManager::find_vacant_remote_name(const std::string& name) { diff --git a/applications/irda/irda-app-remote-manager.h b/applications/irda/irda-app-remote-manager.h index 60993b30..b2a0197f 100644 --- a/applications/irda/irda-app-remote-manager.h +++ b/applications/irda/irda-app-remote-manager.h @@ -1,12 +1,14 @@ #pragma once + +#include "irda-app-signal.h" + #include <irda_worker.h> -#include <stdint.h> +#include <irda.h> + +#include <cstdint> #include <string> -#include <vector> #include <memory> -#include <irda.h> -#include <storage/storage.h> -#include "irda-app-signal.h" +#include <vector> class IrdaAppRemoteButton { friend class IrdaAppRemoteManager; @@ -40,13 +42,13 @@ public: }; class IrdaAppRemoteManager { - static const char* irda_directory; - static const char* irda_extension; std::unique_ptr<IrdaAppRemote> remote; std::string make_full_name(const std::string& remote_name) const; std::string make_remote_name(const std::string& full_name) const; public: + static inline const uint32_t max_button_name_length = 31; + static inline const uint32_t max_remote_name_length = 31; bool add_remote_with_button(const char* button_name, const IrdaAppSignal& signal); bool add_button(const char* button_name, const IrdaAppSignal& signal); diff --git a/applications/irda/irda-app-signal.cpp b/applications/irda/irda-app-signal.cpp index 0a9a7956..45ec3767 100644 --- a/applications/irda/irda-app-signal.cpp +++ b/applications/irda/irda-app-signal.cpp @@ -5,9 +5,9 @@ void IrdaAppSignal::copy_timings(const uint32_t* timings, size_t size) { furi_assert(size); furi_assert(timings); + payload.raw.timings_cnt = size; if(size) { payload.raw.timings = new uint32_t[size]; - payload.raw.timings_cnt = size; memcpy(payload.raw.timings, timings, size * sizeof(uint32_t)); } } diff --git a/applications/irda/irda-app.cpp b/applications/irda/irda-app.cpp index 5dd7c7b4..042d237c 100644 --- a/applications/irda/irda-app.cpp +++ b/applications/irda/irda-app.cpp @@ -48,6 +48,18 @@ int32_t IrdaApp::run(void* args) { return 0; }; +IrdaApp::IrdaApp() { + furi_check(IrdaAppRemoteManager::max_button_name_length < get_text_store_size()); + notification = static_cast<NotificationApp*>(furi_record_open("notification")); + irda_worker = irda_worker_alloc(); +} + +IrdaApp::~IrdaApp() { + irda_worker_free(irda_worker); + furi_record_close("notification"); + for(auto& [key, scene] : scenes) delete scene; +} + IrdaAppViewManager* IrdaApp::get_view_manager() { return &view_manager; } diff --git a/applications/irda/irda-app.h b/applications/irda/irda-app.h index cc7611a4..904bead3 100644 --- a/applications/irda/irda-app.h +++ b/applications/irda/irda-app.h @@ -88,19 +88,12 @@ public: static void text_input_callback(void* context); static void popup_callback(void* context); - IrdaApp() { - notification = static_cast<NotificationApp*>(furi_record_open("notification")); - irda_worker = irda_worker_alloc(); - } - ~IrdaApp() { - irda_worker_free(irda_worker); - furi_record_close("notification"); - for(auto& it : scenes) delete it.second; - } + IrdaApp(); + ~IrdaApp(); private: - static const uint8_t text_store_size = 128; - static const uint8_t text_store_max = 2; + static inline const uint8_t text_store_size = 128; + static inline const uint8_t text_store_max = 2; char text_store[text_store_max][text_store_size + 1]; bool learn_new_remote; EditElement element; diff --git a/applications/irda/scene/irda-app-scene-edit-rename.cpp b/applications/irda/scene/irda-app-scene-edit-rename.cpp index d821d03f..39fbf2ba 100644 --- a/applications/irda/scene/irda-app-scene-edit-rename.cpp +++ b/applications/irda/scene/irda-app-scene-edit-rename.cpp @@ -3,24 +3,31 @@ void IrdaAppSceneEditRename::on_enter(IrdaApp* app) { IrdaAppViewManager* view_manager = app->get_view_manager(); TextInput* text_input = view_manager->get_text_input(); + size_t enter_name_length = 0; auto remote_manager = app->get_remote_manager(); if(app->get_edit_element() == IrdaApp::EditElement::Button) { furi_assert(app->get_current_button() != IrdaApp::ButtonNA); auto button_name = remote_manager->get_button_name(app->get_current_button()); - strncpy(app->get_text_store(0), button_name.c_str(), app->get_text_store_size()); + char* buffer_str = app->get_text_store(0); + size_t max_len = IrdaAppRemoteManager::max_button_name_length; + strncpy(buffer_str, button_name.c_str(), max_len); + buffer_str[max_len + 1] = 0; + enter_name_length = max_len; + text_input_set_header_text(text_input, "Name the key"); } else { auto remote_name = remote_manager->get_remote_name(); strncpy(app->get_text_store(0), remote_name.c_str(), app->get_text_store_size()); + enter_name_length = IrdaAppRemoteManager::max_remote_name_length; + text_input_set_header_text(text_input, "Name the remote"); } - text_input_set_header_text(text_input, "Name the key"); text_input_set_result_callback( text_input, IrdaApp::text_input_callback, app, app->get_text_store(0), - app->get_text_store_size(), + enter_name_length, false); view_manager->switch_to(IrdaAppViewManager::ViewType::TextInput); diff --git a/applications/irda/scene/irda-app-scene-learn-enter-name.cpp b/applications/irda/scene/irda-app-scene-learn-enter-name.cpp index 185e7fbc..aa22d620 100644 --- a/applications/irda/scene/irda-app-scene-learn-enter-name.cpp +++ b/applications/irda/scene/irda-app-scene-learn-enter-name.cpp @@ -26,7 +26,7 @@ void IrdaAppSceneLearnEnterName::on_enter(IrdaApp* app) { IrdaApp::text_input_callback, app, app->get_text_store(0), - app->get_text_store_size(), + IrdaAppRemoteManager::max_button_name_length, false); view_manager->switch_to(IrdaAppViewManager::ViewType::TextInput); |