mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-06-10 19:23:31 -07:00
totp fix to latest
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
#include "commands/help/help.h"
|
||||
#include "commands/move/move.h"
|
||||
#include "commands/pin/pin.h"
|
||||
#include "commands/notification/notification.h"
|
||||
|
||||
static void totp_cli_print_unknown_command(const FuriString* unknown_command) {
|
||||
TOTP_CLI_PRINTF(
|
||||
@@ -52,6 +53,8 @@ static void totp_cli_handler(Cli* cli, FuriString* args, void* context) {
|
||||
totp_cli_command_move_handle(plugin_state, args, cli);
|
||||
} else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_PIN) == 0) {
|
||||
totp_cli_command_pin_handle(plugin_state, args, cli);
|
||||
} else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_NOTIFICATION) == 0) {
|
||||
totp_cli_command_notification_handle(plugin_state, args, cli);
|
||||
} else {
|
||||
totp_cli_print_unknown_command(cmd);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#define TOTP_CLI_COMMAND_NAME "totp"
|
||||
|
||||
#define DOCOPT_ARGUMENT(arg) "<" arg ">"
|
||||
#define DOCOPT_MULTIPLE(arg) arg "..."
|
||||
#define DOCOPT_OPTIONAL(param) "[" param "]"
|
||||
#define DOCOPT_REQUIRED(param) "(" param ")"
|
||||
#define DOCOPT_OPTION(option, value) option " " value
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "../../../lib/list/list.h"
|
||||
#include "../../../types/token_info.h"
|
||||
#include "../../../services/config/config.h"
|
||||
#include "../../../services/convert/convert.h"
|
||||
#include "../../cli_helpers.h"
|
||||
#include "../../../ui/scene_director.h"
|
||||
|
||||
@@ -14,21 +15,6 @@
|
||||
#define TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX "-d"
|
||||
#define TOTP_CLI_COMMAND_ADD_ARG_UNSECURE_PREFIX "-u"
|
||||
|
||||
static bool token_info_set_digits_from_str(TokenInfo* token_info, const FuriString* str) {
|
||||
switch(furi_string_get_char(str, 0)) {
|
||||
case '6':
|
||||
token_info->digits = TOTP_6_DIGITS;
|
||||
return true;
|
||||
case '8':
|
||||
token_info->digits = TOTP_8_DIGITS;
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str) {
|
||||
if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME) == 0) {
|
||||
token_info->algo = SHA1;
|
||||
@@ -73,6 +59,7 @@ void totp_cli_command_add_docopt_options() {
|
||||
DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ADD_ARG_ALGO)) " Token hashing algorithm.\r\n");
|
||||
TOTP_CLI_PRINTF(
|
||||
" Could be one of: sha1, sha256, sha512 " DOCOPT_DEFAULT("sha1") "\r\n");
|
||||
cli_nl();
|
||||
TOTP_CLI_PRINTF(" " DOCOPT_OPTION(
|
||||
TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX,
|
||||
DOCOPT_ARGUMENT(
|
||||
@@ -164,7 +151,8 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl
|
||||
TOTP_CLI_PRINTF(
|
||||
"Missed value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX
|
||||
"\"\r\n");
|
||||
} else if(!token_info_set_digits_from_str(token_info, temp_str)) {
|
||||
} else if(!token_info_set_digits_from_int(
|
||||
token_info, CONVERT_CHAR_TO_DIGIT(furi_string_get_char(temp_str, 0)))) {
|
||||
TOTP_CLI_PRINTF(
|
||||
"\"%s\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX
|
||||
"\"\r\n",
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "../timezone/timezone.h"
|
||||
#include "../move/move.h"
|
||||
#include "../pin/pin.h"
|
||||
#include "../notification/notification.h"
|
||||
|
||||
void totp_cli_command_help_docopt_commands() {
|
||||
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_HELP ", " TOTP_CLI_COMMAND_HELP_ALT
|
||||
@@ -27,6 +28,7 @@ void totp_cli_command_help_handle() {
|
||||
totp_cli_command_timezone_docopt_usage();
|
||||
totp_cli_command_move_docopt_usage();
|
||||
totp_cli_command_pin_docopt_usage();
|
||||
totp_cli_command_notification_docopt_usage();
|
||||
cli_nl();
|
||||
TOTP_CLI_PRINTF("Commands:\r\n");
|
||||
totp_cli_command_help_docopt_commands();
|
||||
@@ -36,11 +38,13 @@ void totp_cli_command_help_handle() {
|
||||
totp_cli_command_timezone_docopt_commands();
|
||||
totp_cli_command_move_docopt_commands();
|
||||
totp_cli_command_pin_docopt_commands();
|
||||
totp_cli_command_notification_docopt_commands();
|
||||
cli_nl();
|
||||
TOTP_CLI_PRINTF("Arguments:\r\n");
|
||||
totp_cli_command_add_docopt_arguments();
|
||||
totp_cli_command_delete_docopt_arguments();
|
||||
totp_cli_command_timezone_docopt_arguments();
|
||||
totp_cli_command_notification_docopt_arguments();
|
||||
cli_nl();
|
||||
TOTP_CLI_PRINTF("Options:\r\n");
|
||||
totp_cli_command_add_docopt_options();
|
||||
|
||||
@@ -20,19 +20,6 @@ static char* get_algo_as_cstr(TokenHashAlgo algo) {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
static uint8_t get_digits_as_int(TokenDigitsCount digits) {
|
||||
switch(digits) {
|
||||
case TOTP_6_DIGITS:
|
||||
return 6;
|
||||
case TOTP_8_DIGITS:
|
||||
return 8;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 6;
|
||||
}
|
||||
|
||||
void totp_cli_command_list_docopt_commands() {
|
||||
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_LIST ", " TOTP_CLI_COMMAND_LIST_ALT
|
||||
" List all available tokens\r\n");
|
||||
@@ -59,13 +46,12 @@ void totp_cli_command_list_handle(PluginState* plugin_state, Cli* cli) {
|
||||
uint16_t index = 1;
|
||||
TOTP_LIST_FOREACH(plugin_state->tokens_list, node, {
|
||||
TokenInfo* token_info = (TokenInfo*)node->data;
|
||||
token_info_get_digits_count(token_info);
|
||||
TOTP_CLI_PRINTF(
|
||||
"| %-3" PRIu16 " | %-27.27s | %-6s | %-6" PRIu8 " |\r\n",
|
||||
index,
|
||||
token_info->name,
|
||||
get_algo_as_cstr(token_info->algo),
|
||||
get_digits_as_int(token_info->digits));
|
||||
token_info->digits);
|
||||
index++;
|
||||
});
|
||||
TOTP_CLI_PRINTF("+-----+-----------------------------+--------+--------+\r\n");
|
||||
|
||||
@@ -21,8 +21,6 @@ void totp_cli_command_timezone_docopt_usage() {
|
||||
void totp_cli_command_timezone_docopt_arguments() {
|
||||
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_TIMEZONE_ARG_TIMEZONE
|
||||
" Timezone offset in hours to be set.\r\n");
|
||||
TOTP_CLI_PRINTF(
|
||||
" If not provided then current timezone offset will be printed\r\n");
|
||||
}
|
||||
|
||||
void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* args, Cli* cli) {
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum {
|
||||
typedef uint8_t TotpRollValueOverflowBehavior;
|
||||
|
||||
enum TotpRollValueOverflowBehaviors {
|
||||
/**
|
||||
* @brief Do not change value if it reached constraint
|
||||
*/
|
||||
@@ -12,7 +14,7 @@ typedef enum {
|
||||
* @brief Set value to opposite constraint value if it reached constraint
|
||||
*/
|
||||
RollOverflowBehaviorRoll
|
||||
} TotpRollValueOverflowBehavior;
|
||||
};
|
||||
|
||||
#define TOTP_ROLL_VALUE_FN_HEADER(type, step_type) \
|
||||
void totp_roll_value_##type( \
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include "../types/plugin_state.h"
|
||||
#include "../types/plugin_event.h"
|
||||
#include "totp_scenes_enum.h"
|
||||
|
||||
/**
|
||||
* @brief Activates scene
|
||||
* @param plugin_state application state
|
||||
* @param scene scene to be activated
|
||||
* @param context scene context to be passed to the scene activation method
|
||||
*/
|
||||
void totp_scene_director_activate_scene(
|
||||
PluginState* const plugin_state,
|
||||
Scene scene,
|
||||
const void* context);
|
||||
|
||||
/**
|
||||
* @brief Deactivate current scene
|
||||
* @param plugin_state application state
|
||||
*/
|
||||
void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state);
|
||||
|
||||
/**
|
||||
* @brief Initializes all the available scenes
|
||||
* @param plugin_state application state
|
||||
*/
|
||||
void totp_scene_director_init_scenes(PluginState* const plugin_state);
|
||||
|
||||
/**
|
||||
* @brief Renders current scene
|
||||
* @param canvas canvas to render at
|
||||
* @param plugin_state application state
|
||||
*/
|
||||
void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_state);
|
||||
|
||||
/**
|
||||
* @brief Disposes all the available scenes
|
||||
* @param plugin_state application state
|
||||
*/
|
||||
void totp_scene_director_dispose(const PluginState* const plugin_state);
|
||||
|
||||
/**
|
||||
* @brief Handles application event for the current scene
|
||||
* @param event event to be handled
|
||||
* @param plugin_state application state
|
||||
* @return \c true if event handled and applilcation should continue; \c false if application should be closed
|
||||
*/
|
||||
bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* const plugin_state);
|
||||
@@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @brief TOTP application scenes
|
||||
*/
|
||||
typedef enum {
|
||||
/**
|
||||
* @brief Empty scene which does nothing
|
||||
*/
|
||||
TotpSceneNone,
|
||||
|
||||
/**
|
||||
* @brief Scene where user have to enter PIN to authenticate
|
||||
*/
|
||||
TotpSceneAuthentication,
|
||||
|
||||
/**
|
||||
* @brief Scene where actual TOTP token is getting generated and displayed to the user
|
||||
*/
|
||||
TotpSceneGenerateToken,
|
||||
|
||||
/**
|
||||
* @brief Scene where user can add new token
|
||||
*/
|
||||
TotpSceneAddNewToken,
|
||||
|
||||
/**
|
||||
* @brief Scene with a menu for given token, allowing user to do multiple actions
|
||||
*/
|
||||
TotpSceneTokenMenu,
|
||||
|
||||
/**
|
||||
* @brief Scene where user can change application settings
|
||||
*/
|
||||
TotpSceneAppSettings
|
||||
} Scene;
|
||||
@@ -10,32 +10,6 @@
|
||||
#define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf"
|
||||
#define CONFIG_FILE_BACKUP_PATH CONFIG_FILE_PATH ".backup"
|
||||
|
||||
static uint8_t token_info_get_digits_as_int(const TokenInfo* token_info) {
|
||||
switch(token_info->digits) {
|
||||
case TOTP_6_DIGITS:
|
||||
return 6;
|
||||
case TOTP_8_DIGITS:
|
||||
return 8;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 6;
|
||||
}
|
||||
|
||||
static void token_info_set_digits_from_int(TokenInfo* token_info, uint8_t digits) {
|
||||
switch(digits) {
|
||||
case 6:
|
||||
token_info->digits = TOTP_6_DIGITS;
|
||||
break;
|
||||
case 8:
|
||||
token_info->digits = TOTP_8_DIGITS;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static char* token_info_get_algo_as_cstr(const TokenInfo* token_info) {
|
||||
switch(token_info->algo) {
|
||||
case SHA1:
|
||||
@@ -106,6 +80,15 @@ FlipperFormat* totp_open_config_file(Storage* storage) {
|
||||
fff_data_file,
|
||||
"Timezone offset in hours. Important note: do not put '+' sign for positive values");
|
||||
flipper_format_write_float(fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &tmp_tz, 1);
|
||||
|
||||
uint32_t tmp_uint32 = NotificationMethodSound | NotificationMethodVibro;
|
||||
flipper_format_write_comment_cstr(fff_data_file, " ");
|
||||
flipper_format_write_comment_cstr(
|
||||
fff_data_file,
|
||||
"How to notify user when new token is generated or badusb mode is activated (possible values: 0 - do not notify, 1 - sound, 2 - vibro, 3 sound and vibro)");
|
||||
flipper_format_write_uint32(
|
||||
fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
|
||||
flipper_format_write_comment_cstr(fff_data_file, " ");
|
||||
@@ -172,8 +155,8 @@ void totp_config_file_save_new_token_i(FlipperFormat* file, const TokenInfo* tok
|
||||
}
|
||||
flipper_format_write_string_cstr(
|
||||
file, TOTP_CONFIG_KEY_TOKEN_ALGO, token_info_get_algo_as_cstr(token_info));
|
||||
uint32_t digits_count_as_uint32 = token_info_get_digits_as_int(token_info);
|
||||
flipper_format_write_uint32(file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &digits_count_as_uint32, 1);
|
||||
uint32_t tmp_uint32 = token_info->digits;
|
||||
flipper_format_write_uint32(file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &tmp_uint32, 1);
|
||||
}
|
||||
|
||||
void totp_config_file_save_new_token(const TokenInfo* token_info) {
|
||||
@@ -196,6 +179,32 @@ void totp_config_file_update_timezone_offset(float new_timezone_offset) {
|
||||
totp_close_storage();
|
||||
}
|
||||
|
||||
void totp_config_file_update_notification_method(NotificationMethod new_notification_method) {
|
||||
Storage* cfg_storage = totp_open_storage();
|
||||
FlipperFormat* file = totp_open_config_file(cfg_storage);
|
||||
|
||||
uint32_t tmp_uint32 = new_notification_method;
|
||||
flipper_format_insert_or_update_uint32(
|
||||
file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1);
|
||||
|
||||
totp_close_config_file(file);
|
||||
totp_close_storage();
|
||||
}
|
||||
|
||||
void totp_config_file_update_user_settings(const PluginState* plugin_state) {
|
||||
Storage* cfg_storage = totp_open_storage();
|
||||
FlipperFormat* file = totp_open_config_file(cfg_storage);
|
||||
|
||||
flipper_format_insert_or_update_float(
|
||||
file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1);
|
||||
uint32_t tmp_uint32 = plugin_state->notification_method;
|
||||
flipper_format_insert_or_update_uint32(
|
||||
file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1);
|
||||
|
||||
totp_close_config_file(file);
|
||||
totp_close_storage();
|
||||
}
|
||||
|
||||
void totp_full_save_config_file(const PluginState* const plugin_state) {
|
||||
Storage* storage = totp_open_storage();
|
||||
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
|
||||
@@ -213,6 +222,9 @@ void totp_full_save_config_file(const PluginState* const plugin_state) {
|
||||
flipper_format_write_float(
|
||||
fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1);
|
||||
flipper_format_write_bool(fff_data_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1);
|
||||
uint32_t tmp_uint32 = plugin_state->notification_method;
|
||||
flipper_format_write_uint32(
|
||||
fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1);
|
||||
|
||||
TOTP_LIST_FOREACH(plugin_state->tokens_list, node, {
|
||||
const TokenInfo* token_info = node->data;
|
||||
@@ -320,6 +332,16 @@ void totp_config_file_load_base(PluginState* const plugin_state) {
|
||||
plugin_state->pin_set = true;
|
||||
}
|
||||
|
||||
flipper_format_rewind(fff_data_file);
|
||||
|
||||
uint32_t tmp_uint32;
|
||||
if(!flipper_format_read_uint32(
|
||||
fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) {
|
||||
tmp_uint32 = NotificationMethodSound | NotificationMethodVibro;
|
||||
}
|
||||
|
||||
plugin_state->notification_method = tmp_uint32;
|
||||
|
||||
furi_string_free(temp_str);
|
||||
totp_close_config_file(fff_data_file);
|
||||
totp_close_storage();
|
||||
@@ -408,10 +430,9 @@ TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state)
|
||||
tokenInfo->algo = SHA1;
|
||||
}
|
||||
|
||||
if(flipper_format_read_uint32(
|
||||
fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &temp_data32, 1)) {
|
||||
token_info_set_digits_from_int(tokenInfo, temp_data32);
|
||||
} else {
|
||||
if(!flipper_format_read_uint32(
|
||||
fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &temp_data32, 1) ||
|
||||
!token_info_set_digits_from_int(tokenInfo, temp_data32)) {
|
||||
tokenInfo->digits = TOTP_6_DIGITS;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,12 @@
|
||||
#include "../../types/token_info.h"
|
||||
#include "constants.h"
|
||||
|
||||
typedef uint8_t TokenLoadingResult;
|
||||
|
||||
/**
|
||||
* @brief Token loading results
|
||||
*/
|
||||
typedef enum {
|
||||
enum TokenLoadingResults {
|
||||
/**
|
||||
* @brief All the tokens loaded successfully
|
||||
*/
|
||||
@@ -24,7 +26,7 @@ typedef enum {
|
||||
* @brief Tokens not loaded because of error(s)
|
||||
*/
|
||||
TokenLoadingResultError
|
||||
} TokenLoadingResult;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Opens storage record
|
||||
@@ -80,3 +82,15 @@ void totp_config_file_save_new_token(const TokenInfo* token_info);
|
||||
* @param new_timezone_offset new timezone offset to be set
|
||||
*/
|
||||
void totp_config_file_update_timezone_offset(float new_timezone_offset);
|
||||
|
||||
/**
|
||||
* @brief Updates notification method in an application config file
|
||||
* @param new_notification_method new notification method to be set
|
||||
*/
|
||||
void totp_config_file_update_notification_method(NotificationMethod new_notification_method);
|
||||
|
||||
/**
|
||||
* @brief Updates application user settings
|
||||
* @param plugin_state application state
|
||||
*/
|
||||
void totp_config_file_update_user_settings(const PluginState* plugin_state);
|
||||
@@ -11,6 +11,7 @@
|
||||
#define TOTP_CONFIG_KEY_CRYPTO_VERIFY "Crypto"
|
||||
#define TOTP_CONFIG_KEY_BASE_IV "BaseIV"
|
||||
#define TOTP_CONFIG_KEY_PINSET "PinIsSet"
|
||||
#define TOTP_CONFIG_KEY_NOTIFICATION_METHOD "NotificationMethod"
|
||||
|
||||
#define TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME "sha1"
|
||||
#define TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME "sha256"
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef _RSIZE_T_DECLARED
|
||||
typedef uint64_t rsize_t;
|
||||
#define _RSIZE_T_DECLARED
|
||||
#endif
|
||||
#ifndef _ERRNOT_DECLARED
|
||||
typedef int16_t errno_t; //-V677
|
||||
#define _ERRNOT_DECLARED
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Copies the value \p c into each of the first \p n characters of the object pointed to by \p s.
|
||||
* @param s pointer to the object to fill
|
||||
* @param smax size of the destination object
|
||||
* @param c fill byte
|
||||
* @param n number of bytes to fill
|
||||
* @return \c 0 on success; non-zero otherwise
|
||||
*/
|
||||
errno_t memset_s(void* s, rsize_t smax, int c, rsize_t n);
|
||||
@@ -1,94 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
/**
|
||||
* @brief Single linked list node
|
||||
*/
|
||||
typedef struct ListNode {
|
||||
/**
|
||||
* @brief Pointer to the data assigned to the current list node
|
||||
*/
|
||||
void* data;
|
||||
|
||||
/**
|
||||
* @brief Pointer to the next list node
|
||||
*/
|
||||
struct ListNode* next;
|
||||
} ListNode;
|
||||
|
||||
/**
|
||||
* @brief Initializes a new list node head
|
||||
* @param data data to be assigned to the head list node
|
||||
* @return Head list node
|
||||
*/
|
||||
ListNode* list_init_head(void* data);
|
||||
|
||||
/**
|
||||
* @brief Adds new list node to the end of the list
|
||||
* @param head head list node
|
||||
* @param data data to be assigned to the newly added list node
|
||||
* @return Head list node
|
||||
*/
|
||||
ListNode* list_add(
|
||||
ListNode* head,
|
||||
void* data); /* adds element with specified data to the end of the list and returns new head node. */
|
||||
|
||||
/**
|
||||
* @brief Searches list node with the given assigned \p data in the list
|
||||
* @param head head list node
|
||||
* @param data data to be searched
|
||||
* @return List node containing \p data if there is such a node in the list; \c NULL otherwise
|
||||
*/
|
||||
ListNode* list_find(ListNode* head, const void* data);
|
||||
|
||||
/**
|
||||
* @brief Searches list node with the given \p index in the list
|
||||
* @param head head list node
|
||||
* @param index desired list node index
|
||||
* @return List node with the given \p index in the list if there is such a list node; \c NULL otherwise
|
||||
*/
|
||||
ListNode* list_element_at(ListNode* head, uint16_t index);
|
||||
|
||||
/**
|
||||
* @brief Removes list node from the list
|
||||
* @param head head list node
|
||||
* @param ep list node to be removed
|
||||
* @return Head list node
|
||||
*/
|
||||
ListNode* list_remove(ListNode* head, ListNode* ep);
|
||||
|
||||
/**
|
||||
* @brief Removes list node with the given \p index in the list from the list
|
||||
* @param head head list node
|
||||
* @param index index of the node to be removed
|
||||
* @param[out] removed_node_data data which was assigned to the removed list node
|
||||
* @return Head list node
|
||||
*/
|
||||
ListNode* list_remove_at(ListNode* head, uint16_t index, void** removed_node_data);
|
||||
|
||||
/**
|
||||
* @brief Inserts new list node at the given index
|
||||
* @param head head list node
|
||||
* @param index index in the list where the new list node should be inserted
|
||||
* @param data data to be assgned to the new list node
|
||||
* @return Head list node
|
||||
*/
|
||||
ListNode* list_insert_at(ListNode* head, uint16_t index, void* data);
|
||||
|
||||
/**
|
||||
* @brief Disposes all the list nodes in the list
|
||||
* @param head head list node
|
||||
*/
|
||||
void list_free(ListNode* head);
|
||||
|
||||
#define TOTP_LIST_INIT_OR_ADD(head, item, assert) \
|
||||
do { \
|
||||
if(head == NULL) { \
|
||||
head = list_init_head(item); \
|
||||
assert(head != NULL); \
|
||||
} else { \
|
||||
assert(list_add(head, item) != NULL); \
|
||||
} \
|
||||
} while(false)
|
||||
@@ -1,56 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum {
|
||||
/**
|
||||
* @brief Do not change value if it reached constraint
|
||||
*/
|
||||
RollOverflowBehaviorStop,
|
||||
|
||||
/**
|
||||
* @brief Set value to opposite constraint value if it reached constraint
|
||||
*/
|
||||
RollOverflowBehaviorRoll
|
||||
} TotpRollValueOverflowBehavior;
|
||||
|
||||
#define TOTP_ROLL_VALUE_FN_HEADER(type, step_type) \
|
||||
void totp_roll_value_##type( \
|
||||
type* value, \
|
||||
step_type step, \
|
||||
type min, \
|
||||
type max, \
|
||||
TotpRollValueOverflowBehavior overflow_behavior)
|
||||
|
||||
/**
|
||||
* @brief Rolls \c int8_t \p value using \p min and \p max as an value constraints with \p step step.
|
||||
* When value reaches constraint value \p overflow_behavior defines what to do next.
|
||||
* @param[in,out] value value to roll
|
||||
* @param step step to be used to change value
|
||||
* @param min minimal possible value
|
||||
* @param max maximum possible value
|
||||
* @param overflow_behavior defines what to do when value reaches constraint value
|
||||
*/
|
||||
TOTP_ROLL_VALUE_FN_HEADER(int8_t, int8_t);
|
||||
|
||||
/**
|
||||
* @brief Rolls \c uint8_t \p value using \p min and \p max as an value constraints with \p step step.
|
||||
* When value reaches constraint value \p overflow_behavior defines what to do next.
|
||||
* @param[in,out] value value to roll
|
||||
* @param step step to be used to change value
|
||||
* @param min minimal possible value
|
||||
* @param max maximum possible value
|
||||
* @param overflow_behavior defines what to do when value reaches constraint value
|
||||
*/
|
||||
TOTP_ROLL_VALUE_FN_HEADER(uint8_t, int8_t);
|
||||
|
||||
/**
|
||||
* @brief Rolls \c uint16_t \p value using \p min and \p max as an value constraints with \p step step.
|
||||
* When value reaches constraint value \p overflow_behavior defines what to do next.
|
||||
* @param[in,out] value value to roll
|
||||
* @param step step to be used to change value
|
||||
* @param min minimal possible value
|
||||
* @param max maximum possible value
|
||||
* @param overflow_behavior defines what to do when value reaches constraint value
|
||||
*/
|
||||
TOTP_ROLL_VALUE_FN_HEADER(uint16_t, int16_t);
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
/**
|
||||
* @brief Calculates timezone offset in seconds given timezone offset in hours.
|
||||
* @param hours timezone offset in hours
|
||||
* @return Timezone offset in seconds.
|
||||
*/
|
||||
int32_t timezone_offset_from_hours(float hours);
|
||||
|
||||
/**
|
||||
* @brief Applies timezone offset to a given time.
|
||||
* @param time time to apply offset to.
|
||||
* @param offset timezone offset in seconds.
|
||||
* @return Time with timezone offset applied.
|
||||
*/
|
||||
uint64_t timezone_offset_apply(uint64_t time, int32_t offset);
|
||||
@@ -1,53 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <gui/gui.h>
|
||||
|
||||
/**
|
||||
* @brief Renders TextBox control
|
||||
* @param canvas canvas to render control at
|
||||
* @param y vertical position of a control to be rendered at
|
||||
* @param text text to be rendered inside control
|
||||
* @param is_selected whether control should be rendered as focused or not
|
||||
*/
|
||||
void ui_control_text_box_render(
|
||||
Canvas* const canvas,
|
||||
int16_t y,
|
||||
const char* text,
|
||||
bool is_selected);
|
||||
|
||||
/**
|
||||
* @brief Renders Button control
|
||||
* @param canvas canvas to render control at
|
||||
* @param x horizontal position of a control to be rendered at
|
||||
* @param y vertical position of a control to be rendered at
|
||||
* @param width control width
|
||||
* @param height control height
|
||||
* @param text text to be rendered inside control
|
||||
* @param is_selected whether control should be rendered as focused or not
|
||||
*/
|
||||
void ui_control_button_render(
|
||||
Canvas* const canvas,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t width,
|
||||
uint8_t height,
|
||||
const char* text,
|
||||
bool is_selected);
|
||||
|
||||
/**
|
||||
* @brief Renders Select control
|
||||
* @param canvas canvas to render control at
|
||||
* @param x horizontal position of a control to be rendered at
|
||||
* @param y vertical position of a control to be rendered at
|
||||
* @param width control width
|
||||
* @param text text to be rendered inside control
|
||||
* @param is_selected whether control should be rendered as focused or not
|
||||
*/
|
||||
void ui_control_select_render(
|
||||
Canvas* const canvas,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t width,
|
||||
const char* text,
|
||||
bool is_selected);
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
static void render_callback(Canvas* const canvas, void* ctx) {
|
||||
PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25);
|
||||
if(plugin_state != NULL && !plugin_state->changing_scene) {
|
||||
if(plugin_state != NULL) {
|
||||
totp_scene_director_render(canvas, plugin_state);
|
||||
}
|
||||
|
||||
@@ -38,8 +38,8 @@ static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queu
|
||||
|
||||
static bool totp_plugin_state_init(PluginState* const plugin_state) {
|
||||
plugin_state->gui = furi_record_open(RECORD_GUI);
|
||||
plugin_state->notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
plugin_state->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
plugin_state->notification_app = furi_record_open(RECORD_NOTIFICATION);
|
||||
plugin_state->dialogs_app = furi_record_open(RECORD_DIALOGS);
|
||||
|
||||
totp_config_file_load_base(plugin_state);
|
||||
|
||||
@@ -57,7 +57,8 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) {
|
||||
SCREEN_HEIGHT_CENTER,
|
||||
AlignCenter,
|
||||
AlignCenter);
|
||||
DialogMessageButton dialog_result = dialog_message_show(plugin_state->dialogs, message);
|
||||
DialogMessageButton dialog_result =
|
||||
dialog_message_show(plugin_state->dialogs_app, message);
|
||||
dialog_message_free(message);
|
||||
if(dialog_result == DialogMessageButtonRight) {
|
||||
totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL);
|
||||
@@ -84,7 +85,7 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) {
|
||||
SCREEN_HEIGHT_CENTER,
|
||||
AlignCenter,
|
||||
AlignCenter);
|
||||
dialog_message_show(plugin_state->dialogs, message);
|
||||
dialog_message_show(plugin_state->dialogs_app, message);
|
||||
dialog_message_free(message);
|
||||
return false;
|
||||
}
|
||||
@@ -150,7 +151,6 @@ int32_t totp_app() {
|
||||
bool processing = true;
|
||||
uint32_t last_user_interaction_time = furi_get_tick();
|
||||
while(processing) {
|
||||
if(plugin_state->changing_scene) continue;
|
||||
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
|
||||
|
||||
PluginState* plugin_state_m = acquire_mutex_block(&state_mutex);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
#include <inttypes.h>
|
||||
|
||||
typedef enum {
|
||||
typedef uint8_t EventType;
|
||||
|
||||
enum EventTypes {
|
||||
EventTypeTick,
|
||||
EventTypeKey,
|
||||
} EventType;
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <dialogs/dialogs.h>
|
||||
#include "../lib/list/list.h"
|
||||
#include "../ui/totp_scenes_enum.h"
|
||||
#include "notification_method.h"
|
||||
|
||||
#define TOTP_IV_SIZE 16
|
||||
|
||||
@@ -22,20 +23,15 @@ typedef struct {
|
||||
*/
|
||||
void* current_scene_state;
|
||||
|
||||
/**
|
||||
* @brief Whether scene is changing now
|
||||
*/
|
||||
bool changing_scene;
|
||||
|
||||
/**
|
||||
* @brief Reference to the firmware notification subsystem
|
||||
*/
|
||||
NotificationApp* notification;
|
||||
NotificationApp* notification_app;
|
||||
|
||||
/**
|
||||
* @brief Reference to the firmware dialogs subsystem
|
||||
*/
|
||||
DialogsApp* dialogs;
|
||||
DialogsApp* dialogs_app;
|
||||
|
||||
/**
|
||||
* @brief Reference to the firmware GUI subsystem
|
||||
@@ -86,4 +82,9 @@ typedef struct {
|
||||
* @brief Basic randomly-generated initialization vector (IV)
|
||||
*/
|
||||
uint8_t base_iv[TOTP_IV_SIZE];
|
||||
|
||||
/**
|
||||
* @brief Notification method
|
||||
*/
|
||||
NotificationMethod notification_method;
|
||||
} PluginState;
|
||||
|
||||
@@ -45,15 +45,17 @@ bool token_info_set_secret(
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t token_info_get_digits_count(const TokenInfo* token_info) {
|
||||
switch(token_info->digits) {
|
||||
case TOTP_6_DIGITS:
|
||||
return 6;
|
||||
case TOTP_8_DIGITS:
|
||||
return 8;
|
||||
bool token_info_set_digits_from_int(TokenInfo* token_info, uint8_t digits) {
|
||||
switch(digits) {
|
||||
case 6:
|
||||
token_info->digits = TOTP_6_DIGITS;
|
||||
return true;
|
||||
case 8:
|
||||
token_info->digits = TOTP_8_DIGITS;
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 6;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
typedef uint8_t TokenHashAlgo;
|
||||
typedef uint8_t TokenDigitsCount;
|
||||
|
||||
/**
|
||||
* @brief Hashing algorithm to be used to generate token
|
||||
*/
|
||||
typedef enum {
|
||||
enum TokenHashAlgos {
|
||||
/**
|
||||
* @brief SHA1 hashing algorithm
|
||||
*/
|
||||
@@ -20,22 +23,22 @@ typedef enum {
|
||||
* @brief SHA512 hashing algorithm
|
||||
*/
|
||||
SHA512
|
||||
} TokenHashAlgo;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Token digits count to be generated.
|
||||
*/
|
||||
typedef enum {
|
||||
enum TokenDigitsCounts {
|
||||
/**
|
||||
* @brief 6 digits
|
||||
*/
|
||||
TOTP_6_DIGITS,
|
||||
TOTP_6_DIGITS = 6,
|
||||
|
||||
/**
|
||||
* @brief 8 digits
|
||||
*/
|
||||
TOTP_8_DIGITS
|
||||
} TokenDigitsCount;
|
||||
TOTP_8_DIGITS = 8
|
||||
};
|
||||
|
||||
#define TOTP_TOKEN_DIGITS_MAX_COUNT 8
|
||||
|
||||
@@ -96,8 +99,9 @@ bool token_info_set_secret(
|
||||
const uint8_t* iv);
|
||||
|
||||
/**
|
||||
* @brief Gets token digits count as \c uint8_t type
|
||||
* @param token_info instance which's desired digits count should be returned
|
||||
* @return Token digits length as \c uint8_t type
|
||||
* @brief Sets token digits count from \c uint8_t value
|
||||
* @param token_info instance whichs token digits count length should be updated
|
||||
* @param digits desired token digits count length
|
||||
* @return \c true if token digits count length has been updated; \c false p
|
||||
*/
|
||||
uint8_t token_info_get_digits_count(const TokenInfo* token_info);
|
||||
bool token_info_set_digits_from_int(TokenInfo* token_info, uint8_t digits);
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum uint8_t {
|
||||
typedef uint8_t TotpUserPinCode;
|
||||
|
||||
enum TotpUserPinCodes {
|
||||
PinCodeArrowUp = 2,
|
||||
PinCodeArrowRight = 8,
|
||||
PinCodeArrowDown = 11,
|
||||
PinCodeArrowLeft = 5
|
||||
} TotpUserPinCode;
|
||||
};
|
||||
@@ -10,7 +10,6 @@ void totp_scene_director_activate_scene(
|
||||
PluginState* const plugin_state,
|
||||
Scene scene,
|
||||
const void* context) {
|
||||
plugin_state->changing_scene = true;
|
||||
totp_scene_director_deactivate_active_scene(plugin_state);
|
||||
switch(scene) {
|
||||
case TotpSceneGenerateToken:
|
||||
@@ -35,7 +34,6 @@ void totp_scene_director_activate_scene(
|
||||
}
|
||||
|
||||
plugin_state->current_scene = scene;
|
||||
plugin_state->changing_scene = false;
|
||||
}
|
||||
|
||||
void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state) {
|
||||
|
||||
@@ -11,10 +11,9 @@
|
||||
#include "../../../types/nullable.h"
|
||||
#include "../generate_token/totp_scene_generate_token.h"
|
||||
|
||||
#define TOKEN_ALGO_LIST_LENGTH 3
|
||||
char* TOKEN_ALGO_LIST[] = {"SHA1", "SHA256", "SHA512"};
|
||||
#define TOKEN_DIGITS_LIST_LENGTH 2
|
||||
char* TOKEN_DIGITS_LIST[] = {"6 digits", "8 digits"};
|
||||
char* TOKEN_DIGITS_TEXT_LIST[] = {"6 digits", "8 digits"};
|
||||
TokenDigitsCount TOKEN_DIGITS_VALUE_LIST[] = {TOTP_6_DIGITS, TOTP_8_DIGITS};
|
||||
|
||||
typedef enum {
|
||||
TokenNameTextBox,
|
||||
@@ -38,7 +37,7 @@ typedef struct {
|
||||
TotpNullable_uint16_t current_token_index;
|
||||
int16_t screen_y_offset;
|
||||
TokenHashAlgo algo;
|
||||
TokenDigitsCount digits_count;
|
||||
uint8_t digits_count_index;
|
||||
} SceneState;
|
||||
|
||||
void totp_scene_add_new_token_init(const PluginState* plugin_state) {
|
||||
@@ -126,7 +125,7 @@ void totp_scene_add_new_token_render(Canvas* const canvas, PluginState* plugin_s
|
||||
0,
|
||||
63 - scene_state->screen_y_offset,
|
||||
SCREEN_WIDTH,
|
||||
TOKEN_DIGITS_LIST[scene_state->digits_count],
|
||||
TOKEN_DIGITS_TEXT_LIST[scene_state->digits_count_index],
|
||||
scene_state->selected_control == TokenLengthSelect);
|
||||
ui_control_button_render(
|
||||
canvas,
|
||||
@@ -196,11 +195,7 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState
|
||||
totp_roll_value_uint8_t(&scene_state->algo, 1, SHA1, SHA512, RollOverflowBehaviorRoll);
|
||||
} else if(scene_state->selected_control == TokenLengthSelect) {
|
||||
totp_roll_value_uint8_t(
|
||||
&scene_state->digits_count,
|
||||
1,
|
||||
TOTP_6_DIGITS,
|
||||
TOTP_8_DIGITS,
|
||||
RollOverflowBehaviorRoll);
|
||||
&scene_state->digits_count_index, 1, 0, 1, RollOverflowBehaviorRoll);
|
||||
}
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
@@ -209,11 +204,7 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState
|
||||
&scene_state->algo, -1, SHA1, SHA512, RollOverflowBehaviorRoll);
|
||||
} else if(scene_state->selected_control == TokenLengthSelect) {
|
||||
totp_roll_value_uint8_t(
|
||||
&scene_state->digits_count,
|
||||
-1,
|
||||
TOTP_6_DIGITS,
|
||||
TOTP_8_DIGITS,
|
||||
RollOverflowBehaviorRoll);
|
||||
&scene_state->digits_count_index, -1, 0, 1, RollOverflowBehaviorRoll);
|
||||
}
|
||||
break;
|
||||
case InputKeyOk:
|
||||
@@ -252,7 +243,7 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState
|
||||
strlcpy(
|
||||
tokenInfo->name, scene_state->token_name, scene_state->token_name_length + 1);
|
||||
tokenInfo->algo = scene_state->algo;
|
||||
tokenInfo->digits = scene_state->digits_count;
|
||||
tokenInfo->digits = TOKEN_DIGITS_VALUE_LIST[scene_state->digits_count_index];
|
||||
|
||||
TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, tokenInfo, furi_check);
|
||||
plugin_state->tokens_count++;
|
||||
@@ -274,7 +265,7 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState
|
||||
SCREEN_HEIGHT_CENTER,
|
||||
AlignCenter,
|
||||
AlignCenter);
|
||||
dialog_message_show(plugin_state->dialogs, message);
|
||||
dialog_message_show(plugin_state->dialogs_app, message);
|
||||
dialog_message_free(message);
|
||||
scene_state->selected_control = TokenSecretTextBox;
|
||||
update_screen_y_offset(scene_state);
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
#include "totp_app_settings.h"
|
||||
#include <math.h>
|
||||
#include <totp_icons.h>
|
||||
#include "../../ui_controls.h"
|
||||
#include "../../scene_director.h"
|
||||
#include "../token_menu/totp_scene_token_menu.h"
|
||||
#include "../../constants.h"
|
||||
#include "../../../services/config/config.h"
|
||||
#include "../../../services/convert/convert.h"
|
||||
#include "../../../lib/roll_value/roll_value.h"
|
||||
#include "../../../types/nullable.h"
|
||||
|
||||
#define DIGIT_TO_CHAR(digit) ((digit) + '0')
|
||||
char* YES_NO_LIST[] = {"NO", "YES"};
|
||||
|
||||
typedef enum { HoursInput, MinutesInput, ConfirmButton } Control;
|
||||
typedef enum { HoursInput, MinutesInput, Sound, Vibro, ConfirmButton } Control;
|
||||
|
||||
typedef struct {
|
||||
int8_t tz_offset_hours;
|
||||
uint8_t tz_offset_minutes;
|
||||
bool notification_sound;
|
||||
bool notification_vibro;
|
||||
uint8_t y_offset;
|
||||
TotpNullable_uint16_t current_token_index;
|
||||
Control selected_control;
|
||||
} SceneState;
|
||||
@@ -39,55 +44,87 @@ void totp_scene_app_settings_activate(
|
||||
float off_dec = modff(plugin_state->timezone_offset, &off_int);
|
||||
scene_state->tz_offset_hours = off_int;
|
||||
scene_state->tz_offset_minutes = 60.0f * off_dec;
|
||||
scene_state->notification_sound = plugin_state->notification_method & NotificationMethodSound;
|
||||
scene_state->notification_vibro = plugin_state->notification_method & NotificationMethodVibro;
|
||||
}
|
||||
|
||||
static void two_digit_to_str(int8_t num, char* str) {
|
||||
uint8_t index = 0;
|
||||
if(num < 0) {
|
||||
str[0] = '-';
|
||||
index++;
|
||||
str[index++] = '-';
|
||||
num = -num;
|
||||
}
|
||||
|
||||
uint8_t d1 = (num / 10) % 10;
|
||||
uint8_t d2 = num % 10;
|
||||
str[index] = DIGIT_TO_CHAR(d1);
|
||||
str[index + 1] = DIGIT_TO_CHAR(d2);
|
||||
str[index + 2] = '\0';
|
||||
str[index++] = CONVERT_DIGIT_TO_CHAR(d1);
|
||||
str[index++] = CONVERT_DIGIT_TO_CHAR(d2);
|
||||
str[index++] = '\0';
|
||||
}
|
||||
|
||||
void totp_scene_app_settings_render(Canvas* const canvas, PluginState* plugin_state) {
|
||||
const SceneState* scene_state = (SceneState*)plugin_state->current_scene_state;
|
||||
void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plugin_state) {
|
||||
const SceneState* scene_state = plugin_state->current_scene_state;
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, "Timezone offset");
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 0, 0 - scene_state->y_offset, AlignLeft, AlignTop, "Timezone offset");
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
char tmp_str[4];
|
||||
two_digit_to_str(scene_state->tz_offset_hours, &tmp_str[0]);
|
||||
canvas_draw_str_aligned(canvas, 0, 16, AlignLeft, AlignTop, "Hours:");
|
||||
canvas_draw_str_aligned(canvas, 0, 16 - scene_state->y_offset, AlignLeft, AlignTop, "Hours:");
|
||||
ui_control_select_render(
|
||||
canvas,
|
||||
36,
|
||||
10,
|
||||
10 - scene_state->y_offset,
|
||||
SCREEN_WIDTH - 36,
|
||||
&tmp_str[0],
|
||||
scene_state->selected_control == HoursInput);
|
||||
|
||||
two_digit_to_str(scene_state->tz_offset_minutes, &tmp_str[0]);
|
||||
canvas_draw_str_aligned(canvas, 0, 34, AlignLeft, AlignTop, "Minutes:");
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 0, 34 - scene_state->y_offset, AlignLeft, AlignTop, "Minutes:");
|
||||
ui_control_select_render(
|
||||
canvas,
|
||||
36,
|
||||
28,
|
||||
28 - scene_state->y_offset,
|
||||
SCREEN_WIDTH - 36,
|
||||
&tmp_str[0],
|
||||
scene_state->selected_control == MinutesInput);
|
||||
|
||||
canvas_draw_icon(
|
||||
canvas,
|
||||
SCREEN_WIDTH_CENTER - 5,
|
||||
SCREEN_HEIGHT - 5 - scene_state->y_offset,
|
||||
&I_totp_arrow_bottom_10x5);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 0, 64 - scene_state->y_offset, AlignLeft, AlignTop, "Notifications");
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
canvas_draw_str_aligned(canvas, 0, 80 - scene_state->y_offset, AlignLeft, AlignTop, "Sound:");
|
||||
ui_control_select_render(
|
||||
canvas,
|
||||
36,
|
||||
74 - scene_state->y_offset,
|
||||
SCREEN_WIDTH - 36,
|
||||
YES_NO_LIST[scene_state->notification_sound],
|
||||
scene_state->selected_control == Sound);
|
||||
|
||||
canvas_draw_str_aligned(canvas, 0, 98 - scene_state->y_offset, AlignLeft, AlignTop, "Vibro:");
|
||||
ui_control_select_render(
|
||||
canvas,
|
||||
36,
|
||||
92 - scene_state->y_offset,
|
||||
SCREEN_WIDTH - 36,
|
||||
YES_NO_LIST[scene_state->notification_vibro],
|
||||
scene_state->selected_control == Vibro);
|
||||
|
||||
ui_control_button_render(
|
||||
canvas,
|
||||
SCREEN_WIDTH_CENTER - 24,
|
||||
50,
|
||||
115 - scene_state->y_offset,
|
||||
48,
|
||||
13,
|
||||
"Confirm",
|
||||
@@ -114,10 +151,20 @@ bool totp_scene_app_settings_handle_event(
|
||||
HoursInput,
|
||||
ConfirmButton,
|
||||
RollOverflowBehaviorStop);
|
||||
if(scene_state->selected_control > MinutesInput) {
|
||||
scene_state->y_offset = 64;
|
||||
} else {
|
||||
scene_state->y_offset = 0;
|
||||
}
|
||||
break;
|
||||
case InputKeyDown:
|
||||
totp_roll_value_uint8_t(
|
||||
&scene_state->selected_control, 1, HoursInput, ConfirmButton, RollOverflowBehaviorStop);
|
||||
if(scene_state->selected_control > MinutesInput) {
|
||||
scene_state->y_offset = 64;
|
||||
} else {
|
||||
scene_state->y_offset = 0;
|
||||
}
|
||||
break;
|
||||
case InputKeyRight:
|
||||
if(scene_state->selected_control == HoursInput) {
|
||||
@@ -126,6 +173,10 @@ bool totp_scene_app_settings_handle_event(
|
||||
} else if(scene_state->selected_control == MinutesInput) {
|
||||
totp_roll_value_uint8_t(
|
||||
&scene_state->tz_offset_minutes, 15, 0, 45, RollOverflowBehaviorRoll);
|
||||
} else if(scene_state->selected_control == Sound) {
|
||||
scene_state->notification_sound = !scene_state->notification_sound;
|
||||
} else if(scene_state->selected_control == Vibro) {
|
||||
scene_state->notification_vibro = !scene_state->notification_vibro;
|
||||
}
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
@@ -135,13 +186,23 @@ bool totp_scene_app_settings_handle_event(
|
||||
} else if(scene_state->selected_control == MinutesInput) {
|
||||
totp_roll_value_uint8_t(
|
||||
&scene_state->tz_offset_minutes, -15, 0, 45, RollOverflowBehaviorRoll);
|
||||
} else if(scene_state->selected_control == Sound) {
|
||||
scene_state->notification_sound = !scene_state->notification_sound;
|
||||
} else if(scene_state->selected_control == Vibro) {
|
||||
scene_state->notification_vibro = !scene_state->notification_vibro;
|
||||
}
|
||||
break;
|
||||
case InputKeyOk:
|
||||
if(scene_state->selected_control == ConfirmButton) {
|
||||
plugin_state->timezone_offset = (float)scene_state->tz_offset_hours +
|
||||
(float)scene_state->tz_offset_minutes / 60.0f;
|
||||
totp_config_file_update_timezone_offset(plugin_state->timezone_offset);
|
||||
|
||||
plugin_state->notification_method =
|
||||
(scene_state->notification_sound ? NotificationMethodSound :
|
||||
NotificationMethodNone) |
|
||||
(scene_state->notification_vibro ? NotificationMethodVibro :
|
||||
NotificationMethodNone);
|
||||
totp_config_file_update_user_settings(plugin_state);
|
||||
|
||||
if(!scene_state->current_token_index.is_null) {
|
||||
TokenMenuSceneContext generate_scene_context = {
|
||||
|
||||
@@ -12,7 +12,7 @@ void totp_scene_app_settings_init(const PluginState* plugin_state);
|
||||
void totp_scene_app_settings_activate(
|
||||
PluginState* plugin_state,
|
||||
const AppSettingsSceneContext* context);
|
||||
void totp_scene_app_settings_render(Canvas* const canvas, PluginState* plugin_state);
|
||||
void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plugin_state);
|
||||
bool totp_scene_app_settings_handle_event(
|
||||
const PluginEvent* const event,
|
||||
PluginState* plugin_state);
|
||||
|
||||
@@ -139,7 +139,7 @@ bool totp_scene_authenticate_handle_event(
|
||||
AlignCenter,
|
||||
AlignCenter);
|
||||
dialog_message_set_icon(message, &I_DolphinCommon_56x48, 72, 17);
|
||||
dialog_message_show(plugin_state->dialogs, message);
|
||||
dialog_message_show(plugin_state->dialogs_app, message);
|
||||
dialog_message_free(message);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "../../../services/totp/totp.h"
|
||||
#include "../../../services/config/config.h"
|
||||
#include "../../../services/crypto/crypto.h"
|
||||
#include "../../../services/convert/convert.h"
|
||||
#include "../../../lib/polyfills/memset_s.h"
|
||||
#include "../../../lib/roll_value/roll_value.h"
|
||||
#include "../../scene_director.h"
|
||||
@@ -16,7 +17,6 @@
|
||||
#include "../../../workers/type_code/type_code.h"
|
||||
|
||||
#define TOKEN_LIFETIME 30
|
||||
#define DIGIT_TO_CHAR(digit) ((digit) + '0')
|
||||
|
||||
typedef struct {
|
||||
uint16_t current_token_index;
|
||||
@@ -25,59 +25,107 @@ typedef struct {
|
||||
bool need_token_update;
|
||||
uint32_t last_token_gen_time;
|
||||
TotpTypeCodeWorkerContext* type_code_worker_context;
|
||||
NotificationMessage const** notification_sequence_new_token;
|
||||
NotificationMessage const** notification_sequence_badusb;
|
||||
} SceneState;
|
||||
|
||||
static const NotificationSequence notification_sequence_new_token = {
|
||||
&message_display_backlight_on,
|
||||
&message_green_255,
|
||||
&message_vibro_on,
|
||||
&message_note_c5,
|
||||
&message_delay_50,
|
||||
&message_vibro_off,
|
||||
&message_sound_off,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const NotificationSequence notification_sequence_badusb = {
|
||||
&message_vibro_on,
|
||||
&message_note_d5,
|
||||
&message_delay_50,
|
||||
&message_note_e4,
|
||||
&message_delay_50,
|
||||
&message_note_f3,
|
||||
&message_delay_50,
|
||||
&message_vibro_off,
|
||||
&message_sound_off,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void i_token_to_str(uint32_t i_token_code, char* str, TokenDigitsCount len) {
|
||||
uint8_t str_token_length = 0;
|
||||
if(len == TOTP_8_DIGITS) {
|
||||
str[8] = '\0';
|
||||
str_token_length = 8;
|
||||
} else if(len == TOTP_6_DIGITS) {
|
||||
str[6] = '\0';
|
||||
str_token_length = 6;
|
||||
}
|
||||
|
||||
if(i_token_code == OTP_ERROR) {
|
||||
memset(&str[0], '-', str_token_length);
|
||||
} else {
|
||||
if(len == TOTP_8_DIGITS) {
|
||||
str[7] = DIGIT_TO_CHAR(i_token_code % 10);
|
||||
str[6] = DIGIT_TO_CHAR((i_token_code = i_token_code / 10) % 10);
|
||||
str[5] = DIGIT_TO_CHAR((i_token_code = i_token_code / 10) % 10);
|
||||
} else if(len == TOTP_6_DIGITS) {
|
||||
str[5] = DIGIT_TO_CHAR(i_token_code % 10);
|
||||
static const NotificationSequence*
|
||||
get_notification_sequence_new_token(const PluginState* plugin_state, SceneState* scene_state) {
|
||||
if(scene_state->notification_sequence_new_token == NULL) {
|
||||
uint8_t i = 0;
|
||||
uint8_t length = 4;
|
||||
if(plugin_state->notification_method & NotificationMethodVibro) {
|
||||
length += 2;
|
||||
}
|
||||
|
||||
str[4] = DIGIT_TO_CHAR((i_token_code = i_token_code / 10) % 10);
|
||||
str[3] = DIGIT_TO_CHAR((i_token_code = i_token_code / 10) % 10);
|
||||
str[2] = DIGIT_TO_CHAR((i_token_code = i_token_code / 10) % 10);
|
||||
str[1] = DIGIT_TO_CHAR((i_token_code = i_token_code / 10) % 10);
|
||||
str[0] = DIGIT_TO_CHAR((i_token_code = i_token_code / 10) % 10);
|
||||
if(plugin_state->notification_method & NotificationMethodSound) {
|
||||
length += 2;
|
||||
}
|
||||
|
||||
scene_state->notification_sequence_new_token = malloc(sizeof(void*) * length);
|
||||
furi_check(scene_state->notification_sequence_new_token != NULL);
|
||||
scene_state->notification_sequence_new_token[i++] = &message_display_backlight_on;
|
||||
scene_state->notification_sequence_new_token[i++] = &message_green_255;
|
||||
if(plugin_state->notification_method & NotificationMethodVibro) {
|
||||
scene_state->notification_sequence_new_token[i++] = &message_vibro_on;
|
||||
}
|
||||
|
||||
if(plugin_state->notification_method & NotificationMethodSound) {
|
||||
scene_state->notification_sequence_new_token[i++] = &message_note_c5;
|
||||
}
|
||||
|
||||
scene_state->notification_sequence_new_token[i++] = &message_delay_50;
|
||||
|
||||
if(plugin_state->notification_method & NotificationMethodVibro) {
|
||||
scene_state->notification_sequence_new_token[i++] = &message_vibro_off;
|
||||
}
|
||||
|
||||
if(plugin_state->notification_method & NotificationMethodSound) {
|
||||
scene_state->notification_sequence_new_token[i++] = &message_sound_off;
|
||||
}
|
||||
|
||||
scene_state->notification_sequence_new_token[i++] = NULL;
|
||||
}
|
||||
|
||||
return (NotificationSequence*)scene_state->notification_sequence_new_token;
|
||||
}
|
||||
|
||||
static const NotificationSequence*
|
||||
get_notification_sequence_badusb(const PluginState* plugin_state, SceneState* scene_state) {
|
||||
if(scene_state->notification_sequence_badusb == NULL) {
|
||||
uint8_t i = 0;
|
||||
uint8_t length = 3;
|
||||
if(plugin_state->notification_method & NotificationMethodVibro) {
|
||||
length += 2;
|
||||
}
|
||||
|
||||
if(plugin_state->notification_method & NotificationMethodSound) {
|
||||
length += 6;
|
||||
}
|
||||
|
||||
scene_state->notification_sequence_badusb = malloc(sizeof(void*) * length);
|
||||
furi_check(scene_state->notification_sequence_badusb != NULL);
|
||||
|
||||
scene_state->notification_sequence_badusb[i++] = &message_blue_255;
|
||||
if(plugin_state->notification_method & NotificationMethodVibro) {
|
||||
scene_state->notification_sequence_badusb[i++] = &message_vibro_on;
|
||||
}
|
||||
|
||||
if(plugin_state->notification_method & NotificationMethodSound) {
|
||||
scene_state->notification_sequence_badusb[i++] = &message_note_d5; //-V525
|
||||
scene_state->notification_sequence_badusb[i++] = &message_delay_50;
|
||||
scene_state->notification_sequence_badusb[i++] = &message_note_e4;
|
||||
scene_state->notification_sequence_badusb[i++] = &message_delay_50;
|
||||
scene_state->notification_sequence_badusb[i++] = &message_note_f3;
|
||||
}
|
||||
|
||||
scene_state->notification_sequence_badusb[i++] = &message_delay_50;
|
||||
|
||||
if(plugin_state->notification_method & NotificationMethodVibro) {
|
||||
scene_state->notification_sequence_badusb[i++] = &message_vibro_off;
|
||||
}
|
||||
|
||||
if(plugin_state->notification_method & NotificationMethodSound) {
|
||||
scene_state->notification_sequence_badusb[i++] = &message_sound_off;
|
||||
}
|
||||
|
||||
scene_state->notification_sequence_badusb[i++] = NULL;
|
||||
}
|
||||
|
||||
return (NotificationSequence*)scene_state->notification_sequence_badusb;
|
||||
}
|
||||
|
||||
static void int_token_to_str(uint32_t i_token_code, char* str, TokenDigitsCount len) {
|
||||
if(i_token_code == OTP_ERROR) {
|
||||
memset(&str[0], '-', len);
|
||||
} else {
|
||||
for(int i = len - 1; i >= 0; i--) {
|
||||
str[i] = CONVERT_DIGIT_TO_CHAR(i_token_code % 10);
|
||||
i_token_code = i_token_code / 10;
|
||||
}
|
||||
}
|
||||
|
||||
str[len] = '\0';
|
||||
}
|
||||
|
||||
TOTP_ALGO get_totp_algo_impl(TokenHashAlgo algo) {
|
||||
@@ -137,7 +185,7 @@ void totp_scene_generate_token_activate(
|
||||
AlignCenter);
|
||||
}
|
||||
|
||||
dialog_message_show(plugin_state->dialogs, message);
|
||||
dialog_message_show(plugin_state->dialogs_app, message);
|
||||
dialog_message_free(message);
|
||||
}
|
||||
}
|
||||
@@ -202,10 +250,10 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_
|
||||
uint8_t* key = totp_crypto_decrypt(
|
||||
tokenInfo->token, tokenInfo->token_length, &plugin_state->iv[0], &key_length);
|
||||
|
||||
i_token_to_str(
|
||||
int_token_to_str(
|
||||
totp_at(
|
||||
get_totp_algo_impl(tokenInfo->algo),
|
||||
token_info_get_digits_count(tokenInfo),
|
||||
tokenInfo->digits,
|
||||
key,
|
||||
key_length,
|
||||
curr_ts,
|
||||
@@ -218,13 +266,15 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_
|
||||
} else {
|
||||
furi_mutex_acquire(
|
||||
scene_state->type_code_worker_context->string_sync, FuriWaitForever);
|
||||
i_token_to_str(0, scene_state->last_code, tokenInfo->digits);
|
||||
int_token_to_str(0, scene_state->last_code, tokenInfo->digits);
|
||||
}
|
||||
|
||||
furi_mutex_release(scene_state->type_code_worker_context->string_sync);
|
||||
|
||||
if(is_new_token_time) {
|
||||
notification_message(plugin_state->notification, ¬ification_sequence_new_token);
|
||||
notification_message(
|
||||
plugin_state->notification_app,
|
||||
get_notification_sequence_new_token(plugin_state, scene_state));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,8 +341,10 @@ bool totp_scene_generate_token_handle_event(
|
||||
if(event->input.type == InputTypeLong && event->input.key == InputKeyDown) {
|
||||
scene_state = (SceneState*)plugin_state->current_scene_state;
|
||||
totp_type_code_worker_notify(
|
||||
scene_state->type_code_worker_context, TotpTypeCodeWorkerEvtType);
|
||||
notification_message(plugin_state->notification, ¬ification_sequence_badusb);
|
||||
scene_state->type_code_worker_context, TotpTypeCodeWorkerEventType);
|
||||
notification_message(
|
||||
plugin_state->notification_app,
|
||||
get_notification_sequence_badusb(plugin_state, scene_state));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -347,6 +399,14 @@ void totp_scene_generate_token_deactivate(PluginState* plugin_state) {
|
||||
|
||||
totp_type_code_worker_stop(scene_state->type_code_worker_context);
|
||||
|
||||
if(scene_state->notification_sequence_new_token != NULL) {
|
||||
free(scene_state->notification_sequence_new_token);
|
||||
}
|
||||
|
||||
if(scene_state->notification_sequence_badusb != NULL) {
|
||||
free(scene_state->notification_sequence_badusb);
|
||||
}
|
||||
|
||||
free(scene_state);
|
||||
plugin_state->current_scene_state = NULL;
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginSt
|
||||
AlignCenter,
|
||||
AlignCenter);
|
||||
DialogMessageButton dialog_result =
|
||||
dialog_message_show(plugin_state->dialogs, message);
|
||||
dialog_message_show(plugin_state->dialogs_app, message);
|
||||
dialog_message_free(message);
|
||||
if(dialog_result == DialogMessageButtonRight &&
|
||||
!scene_state->current_token_index.is_null) {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
typedef uint8_t Scene;
|
||||
|
||||
/**
|
||||
* @brief TOTP application scenes
|
||||
*/
|
||||
typedef enum {
|
||||
enum Scenes {
|
||||
/**
|
||||
* @brief Empty scene which does nothing
|
||||
*/
|
||||
@@ -33,4 +35,4 @@ typedef enum {
|
||||
* @brief Scene where user can change application settings
|
||||
*/
|
||||
TotpSceneAppSettings
|
||||
} Scene;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "type_code.h"
|
||||
#include "../../services/convert/convert.h"
|
||||
|
||||
static const uint8_t hid_number_keys[10] = {
|
||||
HID_KEYBOARD_0,
|
||||
@@ -20,7 +21,7 @@ static void totp_type_code_worker_restore_usb_mode(TotpTypeCodeWorkerContext* co
|
||||
}
|
||||
|
||||
static inline bool totp_type_code_worker_stop_requested() {
|
||||
return furi_thread_flags_get() & TotpTypeCodeWorkerEvtStop;
|
||||
return furi_thread_flags_get() & TotpTypeCodeWorkerEventStop;
|
||||
}
|
||||
|
||||
static void totp_type_code_worker_type_code(TotpTypeCodeWorkerContext* context) {
|
||||
@@ -38,7 +39,7 @@ static void totp_type_code_worker_type_code(TotpTypeCodeWorkerContext* context)
|
||||
furi_delay_ms(500);
|
||||
i = 0;
|
||||
while(i < context->string_length && context->string[i] != 0) {
|
||||
uint8_t digit = context->string[i] - '0';
|
||||
uint8_t digit = CONVERT_CHAR_TO_DIGIT(context->string[i]);
|
||||
if(digit > 9) break;
|
||||
uint8_t hid_kb_key = hid_number_keys[digit];
|
||||
furi_hal_hid_kb_press(hid_kb_key);
|
||||
@@ -63,14 +64,14 @@ static int32_t totp_type_code_worker_callback(void* context) {
|
||||
|
||||
while(true) {
|
||||
uint32_t flags = furi_thread_flags_wait(
|
||||
TotpTypeCodeWorkerEvtStop | TotpTypeCodeWorkerEvtType,
|
||||
TotpTypeCodeWorkerEventStop | TotpTypeCodeWorkerEventType,
|
||||
FuriFlagWaitAny,
|
||||
FuriWaitForever);
|
||||
furi_check((flags & FuriFlagError) == 0); //-V562
|
||||
if(flags & TotpTypeCodeWorkerEvtStop) break;
|
||||
if(flags & TotpTypeCodeWorkerEventStop) break;
|
||||
|
||||
TotpTypeCodeWorkerContext* h_context = acquire_mutex_block(&context_mutex);
|
||||
if(flags & TotpTypeCodeWorkerEvtType) {
|
||||
if(flags & TotpTypeCodeWorkerEventType) {
|
||||
totp_type_code_worker_type_code(h_context);
|
||||
}
|
||||
|
||||
@@ -98,7 +99,7 @@ TotpTypeCodeWorkerContext* totp_type_code_worker_start() {
|
||||
|
||||
void totp_type_code_worker_stop(TotpTypeCodeWorkerContext* context) {
|
||||
furi_assert(context != NULL);
|
||||
furi_thread_flags_set(furi_thread_get_id(context->thread), TotpTypeCodeWorkerEvtStop);
|
||||
furi_thread_flags_set(furi_thread_get_id(context->thread), TotpTypeCodeWorkerEventStop);
|
||||
furi_thread_join(context->thread);
|
||||
furi_thread_free(context->thread);
|
||||
furi_mutex_free(context->string_sync);
|
||||
@@ -108,7 +109,7 @@ void totp_type_code_worker_stop(TotpTypeCodeWorkerContext* context) {
|
||||
|
||||
void totp_type_code_worker_notify(
|
||||
TotpTypeCodeWorkerContext* context,
|
||||
TotpTypeCodeWorkerEvtFlags event) {
|
||||
TotpTypeCodeWorkerEvent event) {
|
||||
furi_assert(context != NULL);
|
||||
furi_thread_flags_set(furi_thread_get_id(context->thread), event);
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <furi/furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
typedef uint8_t TotpTypeCodeWorkerEvent;
|
||||
|
||||
typedef struct {
|
||||
char* string;
|
||||
uint8_t string_length;
|
||||
@@ -12,14 +14,14 @@ typedef struct {
|
||||
FuriHalUsbInterface* usb_mode_prev;
|
||||
} TotpTypeCodeWorkerContext;
|
||||
|
||||
typedef enum {
|
||||
TotpTypeCodeWorkerEvtReserved = (1 << 0),
|
||||
TotpTypeCodeWorkerEvtStop = (1 << 1),
|
||||
TotpTypeCodeWorkerEvtType = (1 << 2)
|
||||
} TotpTypeCodeWorkerEvtFlags;
|
||||
enum TotpTypeCodeWorkerEvents {
|
||||
TotpTypeCodeWorkerEventReserved = (1 << 0),
|
||||
TotpTypeCodeWorkerEventStop = (1 << 1),
|
||||
TotpTypeCodeWorkerEventType = (1 << 2)
|
||||
};
|
||||
|
||||
TotpTypeCodeWorkerContext* totp_type_code_worker_start();
|
||||
void totp_type_code_worker_stop(TotpTypeCodeWorkerContext* context);
|
||||
void totp_type_code_worker_notify(
|
||||
TotpTypeCodeWorkerContext* context,
|
||||
TotpTypeCodeWorkerEvtFlags event);
|
||||
TotpTypeCodeWorkerEvent event);
|
||||
Reference in New Issue
Block a user