This commit is contained in:
Willy-JL
2024-06-08 18:16:10 +02:00
27 changed files with 1546 additions and 143 deletions

View File

@@ -0,0 +1,40 @@
Filetype: Flipper NFC device
Version: 4
# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare DESFire, SLIX, ST25TB
Device type: FeliCa
# UID is common for all formats
UID: 29 9F FA 53 AB 75 87 6E
# FeliCa specific data
Data format version: 1
Manufacture id: 29 9F FA 53 AB 75 87 6E
Manufacture parameter: 57 4E 10 2A 94 16 BC 8E
Blocks total: 28
Blocks read: 28
Block 0: 00 00 DE AD BE AF 00 00 00 00 00 00 00 00 DE AD BE AF
Block 1: 00 00 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
Block 2: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 3: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 4: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 5: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 6: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 7: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 9: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 11: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 12: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 13: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 14: 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
Block 15: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 16: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 17: 00 00 29 9F FA 53 AB 75 87 6E 57 4E 10 2A 94 16 BC 8E
Block 18: 00 00 29 9F FA 53 AB 75 87 6E 00 F1 00 00 00 01 43 00
Block 19: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 20: 00 00 88 B4 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 21: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 22: 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
Block 23: 00 00 FF FF FF 00 FF 00 10 00 00 00 00 00 00 00 00 00
Block 24: 00 00 24 FE FF 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 25: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 26: 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 27: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

View File

@@ -12,6 +12,8 @@
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include <nfc/protocols/felica/felica.h>
#include <nfc/protocols/felica/felica_poller_sync.h>
#include <nfc/protocols/mf_classic/mf_classic_poller.h>
#include <nfc/protocols/iso15693_3/iso15693_3_poller.h>
#include <nfc/protocols/slix/slix.h>
@@ -646,6 +648,56 @@ MU_TEST(mf_classic_dict_test) {
"Remove test dict failed");
}
static FelicaError
felica_do_request_response(FelicaData* felica_data, const FelicaCardKey* card_key) {
NfcDeviceData* nfc_device = nfc_device_alloc();
FelicaError error = FelicaErrorNone;
if(!nfc_device_load(nfc_device, EXT_PATH("unit_tests/nfc/Felica.nfc"))) {
error = FelicaErrorNotPresent;
} else {
Nfc* poller = nfc_alloc();
Nfc* listener = nfc_alloc();
NfcListener* felica_listener = nfc_listener_alloc(
listener, NfcProtocolFelica, nfc_device_get_data(nfc_device, NfcProtocolFelica));
nfc_listener_start(felica_listener, NULL, NULL);
error = felica_poller_sync_read(poller, felica_data, card_key);
nfc_listener_stop(felica_listener);
nfc_listener_free(felica_listener);
nfc_free(listener);
nfc_free(poller);
}
nfc_device_free(nfc_device);
return error;
}
MU_TEST(felica_read) {
FelicaData* felica_data = felica_alloc();
FelicaError error = felica_do_request_response(felica_data, NULL);
mu_assert(error == FelicaErrorNone, "felica_poller() failed");
mu_assert(felica_data->data.fs.spad[4].SF1 == 0x01, "block[4].SF1 != 0x01");
mu_assert(felica_data->data.fs.spad[4].SF2 == 0xB1, "block[4].SF2 != 0xB1");
felica_free(felica_data);
}
MU_TEST(felica_read_auth) {
FelicaData* felica_data = felica_alloc();
FelicaCardKey card_key;
memset(card_key.data, 0xFF, FELICA_DATA_BLOCK_SIZE);
FelicaError error = felica_do_request_response(felica_data, &card_key);
mu_assert(error == FelicaErrorNone, "felica_poller() failed");
mu_assert(felica_data->data.fs.spad[4].SF1 == 0x00, "block[4].SF1 != 0x00");
mu_assert(felica_data->data.fs.spad[4].SF2 == 0x00, "block[4].SF2 != 0x00");
felica_free(felica_data);
}
MU_TEST(slix_file_with_capabilities_test) {
NfcDevice* nfc_device_missed_cap = nfc_device_alloc();
mu_assert(
@@ -807,6 +859,8 @@ MU_TEST_SUITE(nfc) {
MU_RUN_TEST(mf_classic_value_block);
MU_RUN_TEST(mf_classic_send_frame_test);
MU_RUN_TEST(mf_classic_dict_test);
MU_RUN_TEST(felica_read);
MU_RUN_TEST(felica_read_auth);
MU_RUN_TEST(slix_file_with_capabilities_test);
MU_RUN_TEST(slix_set_password_default_cap_correct_pass);

View File

@@ -20,6 +20,7 @@ void archive_scene_rename_on_enter(void* context) {
TextInput* text_input = archive->text_input;
ArchiveFile_t* current = archive_get_current_file(archive->browser);
const bool is_file = current->type != ArchiveFileTypeFolder;
FuriString* path_name = furi_string_alloc();
FuriString* path_folder = furi_string_alloc();
@@ -27,16 +28,15 @@ void archive_scene_rename_on_enter(void* context) {
ArchiveTabEnum tab = archive_get_tab(archive->browser);
bool hide_ext = tab != ArchiveTabBrowser && tab != ArchiveTabInternal;
if(current->type == ArchiveFileTypeFolder || !hide_ext) {
furi_string_reset(archive->file_extension);
path_extract_basename(furi_string_get_cstr(current->path), path_name);
} else {
if(is_file && !hide_ext) {
path_extract_ext_str(current->path, archive->file_extension);
path_extract_filename(current->path, path_name, true);
} else {
furi_string_reset(archive->file_extension);
path_extract_basename(furi_string_get_cstr(current->path), path_name);
}
strlcpy(archive->text_store, furi_string_get_cstr(path_name), MAX_NAME_LEN);
text_input_set_header_text(
text_input, current->type == ArchiveFileTypeFolder ? "Rename directory:" : "Rename file:");
text_input_set_header_text(text_input, is_file ? "Rename file:" : "Rename directory:");
// Get current folder (for file) or previous folder (for folder) for validator
path_extract_dirname(furi_string_get_cstr(current->path), path_folder);
@@ -44,7 +44,7 @@ void archive_scene_rename_on_enter(void* context) {
text_input_set_result_callback(
text_input,
archive_scene_rename_text_input_callback,
context,
archive,
archive->text_store,
MAX_NAME_LEN,
false);

View File

@@ -26,12 +26,6 @@ static void nfc_scene_info_on_enter_felica(NfcApp* instance) {
widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 48, furi_string_get_cstr(temp_str));
widget_add_button_element(
instance->widget,
GuiButtonTypeRight,
"More",
nfc_protocol_support_common_widget_callback,
instance);
furi_string_free(temp_str);
}
@@ -177,7 +171,7 @@ static bool nfc_scene_read_menu_on_event_felica(NfcApp* instance, SceneManagerEv
}
const NfcProtocolSupportBase nfc_protocol_support_felica = {
.features = NfcProtocolFeatureEmulateUid,
.features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo,
.scene_info =
{

View File

@@ -52,6 +52,7 @@ typedef enum {
SubGhzRxKeyStateAddKey,
SubGhzRxKeyStateExit,
SubGhzRxKeyStateRAWLoad,
SubGhzRxKeyStateRAWMore,
SubGhzRxKeyStateRAWSave,
SubGhzRxKeyStateTX,
} SubGhzRxKeyState;

View File

@@ -66,7 +66,13 @@ bool subghz_scene_delete_raw_on_event(void* context, SceneManagerEvent event) {
if(event.event == SubGhzCustomEventSceneDeleteRAW) {
furi_string_set(subghz->file_path_tmp, subghz->file_path);
if(subghz_delete_file(subghz)) {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess);
if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateRAWLoad) {
subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess);
} else {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved);
}
} else {
scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneStart);

View File

@@ -80,6 +80,7 @@ void subghz_scene_read_raw_on_enter(void* context) {
subghz->subghz_read_raw, SubGhzReadRAWStatusIDLE, "", threshold_rssi);
break;
case SubGhzRxKeyStateRAWLoad:
case SubGhzRxKeyStateRAWMore:
path_extract_filename(subghz->file_path, file_name, true);
subghz_read_raw_set_status(
subghz->subghz_read_raw,
@@ -101,7 +102,8 @@ void subghz_scene_read_raw_on_enter(void* context) {
break;
}
if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateBack) {
if((subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateBack) &&
(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateRAWLoad)) {
subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);
if(furi_string_empty(file_name)) {
@@ -204,7 +206,9 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
if(subghz_scene_read_raw_update_filename(subghz)) {
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet);
subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWLoad);
if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateRAWLoad) {
subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWMore);
}
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneMoreRAW);
consumed = true;
} else {

View File

@@ -8,6 +8,7 @@
#include <lib/toolbox/md5_calc.h>
#include <lib/toolbox/path.h>
#include <update_util/lfs_backup.h>
#include <toolbox/tar/tar_archive.h>
#include <pb_decode.h>
#include <storage.pb.h>
@@ -50,7 +51,6 @@ static void rpc_system_storage_reset_state(
if(rpc_storage->state == RpcStorageStateWriting) {
storage_file_close(rpc_storage->file);
storage_file_free(rpc_storage->file);
furi_record_close(RECORD_STORAGE);
}
rpc_storage->state = RpcStorageStateIdle;
@@ -118,10 +118,8 @@ static void rpc_system_storage_info_process(const PB_Main* request, void* contex
PB_Main* response = malloc(sizeof(PB_Main));
response->command_id = request->command_id;
Storage* fs_api = furi_record_open(RECORD_STORAGE);
FS_Error error = storage_common_fs_info(
fs_api,
rpc_storage->api,
request->content.storage_info_request.path,
&response->content.storage_info_response.total_space,
&response->content.storage_info_response.free_space);
@@ -135,7 +133,6 @@ static void rpc_system_storage_info_process(const PB_Main* request, void* contex
rpc_send_and_release(session, response);
free(response);
furi_record_close(RECORD_STORAGE);
}
static void rpc_system_storage_timestamp_process(const PB_Main* request, void* context) {
@@ -154,11 +151,9 @@ static void rpc_system_storage_timestamp_process(const PB_Main* request, void* c
PB_Main* response = malloc(sizeof(PB_Main));
response->command_id = request->command_id;
Storage* fs_api = furi_record_open(RECORD_STORAGE);
const char* path = request->content.storage_timestamp_request.path;
uint32_t timestamp = 0;
FS_Error error = storage_common_timestamp(fs_api, path, &timestamp);
FS_Error error = storage_common_timestamp(rpc_storage->api, path, &timestamp);
response->command_status = rpc_system_storage_get_error(error);
response->which_content = PB_Main_empty_tag;
@@ -170,7 +165,6 @@ static void rpc_system_storage_timestamp_process(const PB_Main* request, void* c
rpc_send_and_release(session, response);
free(response);
furi_record_close(RECORD_STORAGE);
}
static void rpc_system_storage_stat_process(const PB_Main* request, void* context) {
@@ -189,11 +183,9 @@ static void rpc_system_storage_stat_process(const PB_Main* request, void* contex
PB_Main* response = malloc(sizeof(PB_Main));
response->command_id = request->command_id;
Storage* fs_api = furi_record_open(RECORD_STORAGE);
const char* path = request->content.storage_stat_request.path;
FileInfo fileinfo;
FS_Error error = storage_common_stat(fs_api, path, &fileinfo);
FS_Error error = storage_common_stat(rpc_storage->api, path, &fileinfo);
response->command_status = rpc_system_storage_get_error(error);
response->which_content = PB_Main_empty_tag;
@@ -209,12 +201,12 @@ static void rpc_system_storage_stat_process(const PB_Main* request, void* contex
rpc_send_and_release(session, response);
free(response);
furi_record_close(RECORD_STORAGE);
}
static void rpc_system_storage_list_root(const PB_Main* request, void* context) {
furi_assert(request);
furi_assert(context);
RpcStorageSystem* rpc_storage = context;
RpcSession* session = rpc_storage->session;
furi_assert(session);
@@ -279,8 +271,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex
return;
}
Storage* fs_api = furi_record_open(RECORD_STORAGE);
File* dir = storage_file_alloc(fs_api);
File* dir = storage_file_alloc(rpc_storage->api);
PB_Main response = {
.command_id = request->command_id,
@@ -293,7 +284,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex
bool include_md5 = list_request->include_md5;
FuriString* md5 = furi_string_alloc();
FuriString* md5_path = furi_string_alloc();
File* file = storage_file_alloc(fs_api);
File* file = storage_file_alloc(rpc_storage->api);
bool finish = false;
int i = 0;
@@ -350,8 +341,6 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex
storage_dir_close(dir);
storage_file_free(dir);
storage_file_free(file);
furi_record_close(RECORD_STORAGE);
}
static void rpc_system_storage_read_process(const PB_Main* request, void* context) {
@@ -370,8 +359,7 @@ static void rpc_system_storage_read_process(const PB_Main* request, void* contex
/* use same message memory to send response */
PB_Main* response = malloc(sizeof(PB_Main));
const char* path = request->content.storage_read_request.path;
Storage* fs_api = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(fs_api);
File* file = storage_file_alloc(rpc_storage->api);
bool fs_operation_success = storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING);
if(fs_operation_success) {
@@ -420,8 +408,6 @@ static void rpc_system_storage_read_process(const PB_Main* request, void* contex
free(response);
storage_file_close(file);
storage_file_free(file);
furi_record_close(RECORD_STORAGE);
}
static void rpc_system_storage_write_process(const PB_Main* request, void* context) {
@@ -451,7 +437,6 @@ static void rpc_system_storage_write_process(const PB_Main* request, void* conte
}
if(rpc_storage->state != RpcStorageStateWriting) {
rpc_storage->api = furi_record_open(RECORD_STORAGE);
rpc_storage->file = storage_file_alloc(rpc_storage->api);
rpc_storage->current_command_id = request->command_id;
rpc_storage->state = RpcStorageStateWriting;
@@ -492,14 +477,15 @@ static void rpc_system_storage_write_process(const PB_Main* request, void* conte
}
}
static bool rpc_system_storage_is_dir_is_empty(Storage* fs_api, const char* path) {
furi_assert(fs_api);
static bool rpc_system_storage_is_dir_is_empty(Storage* storage, const char* path) {
furi_assert(storage);
furi_assert(path);
FileInfo fileinfo;
bool is_dir_is_empty = true;
FS_Error error = storage_common_stat(fs_api, path, &fileinfo);
FS_Error error = storage_common_stat(storage, path, &fileinfo);
if((error == FSE_OK) && file_info_is_dir(&fileinfo)) {
File* dir = storage_file_alloc(fs_api);
File* dir = storage_file_alloc(storage);
if(storage_dir_open(dir, path)) {
char* name = malloc(MAX_NAME_LENGTH);
while(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) {
@@ -531,18 +517,17 @@ static void rpc_system_storage_delete_process(const PB_Main* request, void* cont
PB_CommandStatus status = PB_CommandStatus_ERROR;
rpc_system_storage_reset_state(rpc_storage, session, true);
Storage* fs_api = furi_record_open(RECORD_STORAGE);
char* path = request->content.storage_delete_request.path;
if(!path) {
status = PB_CommandStatus_ERROR_INVALID_PARAMETERS;
} else {
FS_Error error_remove = storage_common_remove(fs_api, path);
FS_Error error_remove = storage_common_remove(rpc_storage->api, path);
// FSE_DENIED is for empty directory, but not only for this
// that's why we have to check it
if((error_remove == FSE_DENIED) && !rpc_system_storage_is_dir_is_empty(fs_api, path)) {
if((error_remove == FSE_DENIED) &&
!rpc_system_storage_is_dir_is_empty(rpc_storage->api, path)) {
if(request->content.storage_delete_request.recursive) {
bool deleted = storage_simply_remove_recursive(fs_api, path);
bool deleted = storage_simply_remove_recursive(rpc_storage->api, path);
status = deleted ? PB_CommandStatus_OK : PB_CommandStatus_ERROR;
} else {
status = PB_CommandStatus_ERROR_STORAGE_DIR_NOT_EMPTY;
@@ -554,7 +539,6 @@ static void rpc_system_storage_delete_process(const PB_Main* request, void* cont
}
}
furi_record_close(RECORD_STORAGE);
rpc_send_and_release_empty(session, request->command_id, status);
}
@@ -572,11 +556,10 @@ static void rpc_system_storage_mkdir_process(const PB_Main* request, void* conte
PB_CommandStatus status;
rpc_system_storage_reset_state(rpc_storage, session, true);
Storage* fs_api = furi_record_open(RECORD_STORAGE);
char* path = request->content.storage_mkdir_request.path;
if(path) {
if(path_contains_only_ascii(path)) {
FS_Error error = storage_common_mkdir(fs_api, path);
FS_Error error = storage_common_mkdir(rpc_storage->api, path);
status = rpc_system_storage_get_error(error);
} else {
status = PB_CommandStatus_ERROR_STORAGE_INVALID_NAME;
@@ -584,7 +567,6 @@ static void rpc_system_storage_mkdir_process(const PB_Main* request, void* conte
} else {
status = PB_CommandStatus_ERROR_INVALID_PARAMETERS;
}
furi_record_close(RECORD_STORAGE);
rpc_send_and_release_empty(session, request->command_id, status);
}
@@ -608,8 +590,7 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont
return;
}
Storage* fs_api = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(fs_api);
File* file = storage_file_alloc(rpc_storage->api);
FuriString* md5 = furi_string_alloc();
FS_Error file_error;
@@ -633,8 +614,6 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont
furi_string_free(md5);
storage_file_free(file);
furi_record_close(RECORD_STORAGE);
}
static void rpc_system_storage_rename_process(const PB_Main* request, void* context) {
@@ -651,11 +630,9 @@ static void rpc_system_storage_rename_process(const PB_Main* request, void* cont
PB_CommandStatus status;
rpc_system_storage_reset_state(rpc_storage, session, true);
Storage* fs_api = furi_record_open(RECORD_STORAGE);
if(path_contains_only_ascii(request->content.storage_rename_request.new_path)) {
FS_Error error = storage_common_rename_safe(
fs_api,
rpc_storage->api,
request->content.storage_rename_request.old_path,
request->content.storage_rename_request.new_path);
status = rpc_system_storage_get_error(error);
@@ -663,7 +640,6 @@ static void rpc_system_storage_rename_process(const PB_Main* request, void* cont
status = PB_CommandStatus_ERROR_STORAGE_INVALID_NAME;
}
furi_record_close(RECORD_STORAGE);
rpc_send_and_release_empty(session, request->command_id, status);
}
@@ -678,12 +654,10 @@ static void rpc_system_storage_backup_create_process(const PB_Main* request, voi
RpcSession* session = rpc_storage->session;
furi_assert(session);
Storage* fs_api = furi_record_open(RECORD_STORAGE);
rpc_system_storage_reset_state(rpc_storage, session, true);
bool backup_ok =
lfs_backup_create(fs_api, request->content.storage_backup_create_request.archive_path);
furi_record_close(RECORD_STORAGE);
bool backup_ok = lfs_backup_create(
rpc_storage->api, request->content.storage_backup_create_request.archive_path);
rpc_send_and_release_empty(
session, request->command_id, backup_ok ? PB_CommandStatus_OK : PB_CommandStatus_ERROR);
@@ -700,17 +674,58 @@ static void rpc_system_storage_backup_restore_process(const PB_Main* request, vo
RpcSession* session = rpc_storage->session;
furi_assert(session);
Storage* fs_api = furi_record_open(RECORD_STORAGE);
rpc_system_storage_reset_state(rpc_storage, session, true);
bool backup_ok =
lfs_backup_unpack(fs_api, request->content.storage_backup_restore_request.archive_path);
furi_record_close(RECORD_STORAGE);
bool backup_ok = lfs_backup_unpack(
rpc_storage->api, request->content.storage_backup_restore_request.archive_path);
rpc_send_and_release_empty(
session, request->command_id, backup_ok ? PB_CommandStatus_OK : PB_CommandStatus_ERROR);
}
static void rpc_system_storage_tar_extract_process(const PB_Main* request, void* context) {
furi_assert(request);
furi_assert(request->which_content == PB_Main_storage_tar_extract_request_tag);
furi_assert(context);
FURI_LOG_D(TAG, "TarExtract");
RpcStorageSystem* rpc_storage = context;
RpcSession* session = rpc_storage->session;
furi_assert(session);
PB_CommandStatus status;
rpc_system_storage_reset_state(rpc_storage, session, true);
TarArchive* archive = tar_archive_alloc(rpc_storage->api);
do {
if(!path_contains_only_ascii(request->content.storage_tar_extract_request.out_path)) {
status = PB_CommandStatus_ERROR_STORAGE_INVALID_NAME;
break;
}
if(!tar_archive_open(
archive,
request->content.storage_tar_extract_request.tar_path,
TAR_OPEN_MODE_READ)) {
status = PB_CommandStatus_ERROR_STORAGE_INVALID_PARAMETER;
break;
}
if(!tar_archive_unpack_to(
archive, request->content.storage_tar_extract_request.out_path, NULL)) {
status = PB_CommandStatus_ERROR_STORAGE_INTERNAL;
break;
}
status = PB_CommandStatus_OK;
} while(0);
tar_archive_free(archive);
rpc_send_and_release_empty(session, request->command_id, status);
}
void* rpc_system_storage_alloc(RpcSession* session) {
furi_assert(session);
@@ -761,6 +776,9 @@ void* rpc_system_storage_alloc(RpcSession* session) {
rpc_handler.message_handler = rpc_system_storage_backup_restore_process;
rpc_add_handler(session, PB_Main_storage_backup_restore_request_tag, &rpc_handler);
rpc_handler.message_handler = rpc_system_storage_tar_extract_process;
rpc_add_handler(session, PB_Main_storage_tar_extract_request_tag, &rpc_handler);
return rpc_storage;
}
@@ -771,5 +789,8 @@ void rpc_system_storage_free(void* context) {
furi_assert(session);
rpc_system_storage_reset_state(rpc_storage, session, false);
furi_record_close(RECORD_STORAGE);
rpc_storage->api = NULL;
free(rpc_storage);
}