mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-17 04:34:44 -07:00
Update TOTP
This commit is contained in:
15
applications/external/totp/application.fam
vendored
15
applications/external/totp/application.fam
vendored
@@ -3,7 +3,16 @@ App(
|
|||||||
name="Authenticator",
|
name="Authenticator",
|
||||||
apptype=FlipperAppType.EXTERNAL,
|
apptype=FlipperAppType.EXTERNAL,
|
||||||
entry_point="totp_app",
|
entry_point="totp_app",
|
||||||
requires=["gui", "cli", "dialogs", "storage", "input", "notification", "bt"],
|
cdefines=["APP_TOTP"],
|
||||||
|
requires=[
|
||||||
|
"gui",
|
||||||
|
"cli",
|
||||||
|
"dialogs",
|
||||||
|
"storage",
|
||||||
|
"input",
|
||||||
|
"notification",
|
||||||
|
"bt"
|
||||||
|
],
|
||||||
stack_size=2 * 1024,
|
stack_size=2 * 1024,
|
||||||
order=20,
|
order=20,
|
||||||
fap_author="Alexander Kopachov (@akopachov)",
|
fap_author="Alexander Kopachov (@akopachov)",
|
||||||
@@ -19,7 +28,9 @@ App(
|
|||||||
Lib(
|
Lib(
|
||||||
name="base64",
|
name="base64",
|
||||||
),
|
),
|
||||||
Lib(name="linked_list"),
|
Lib(
|
||||||
|
name="linked_list"
|
||||||
|
),
|
||||||
Lib(
|
Lib(
|
||||||
name="timezone_utils",
|
name="timezone_utils",
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -121,6 +121,19 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl
|
|||||||
memset(&new_pin[0], 0, TOTP_IV_SIZE);
|
memset(&new_pin[0], 0, TOTP_IV_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char* backup_path = totp_config_file_backup();
|
||||||
|
if(backup_path != NULL) {
|
||||||
|
TOTP_CLI_PRINTF_WARNING("Backup conf file %s has been created\r\n", backup_path);
|
||||||
|
TOTP_CLI_PRINTF_WARNING(
|
||||||
|
"Once you make sure everything is fine and works as expected, please delete this backup file\r\n");
|
||||||
|
free(backup_path);
|
||||||
|
} else {
|
||||||
|
memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE);
|
||||||
|
TOTP_CLI_PRINTF_ERROR(
|
||||||
|
"An error has occurred during taking backup of config file\r\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if(plugin_state->current_scene == TotpSceneGenerateToken) {
|
if(plugin_state->current_scene == TotpSceneGenerateToken) {
|
||||||
totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL);
|
totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL);
|
||||||
load_generate_token_scene = true;
|
load_generate_token_scene = true;
|
||||||
|
|||||||
8
applications/external/totp/features_config.h
vendored
8
applications/external/totp/features_config.h
vendored
@@ -5,10 +5,12 @@
|
|||||||
#define TOTP_AUTOMATION_ICONS_ENABLED
|
#define TOTP_AUTOMATION_ICONS_ENABLED
|
||||||
|
|
||||||
// List of compatible firmwares
|
// List of compatible firmwares
|
||||||
#define TOTP_FIRMWARE_OFFICIAL_STABLE 1
|
#define TOTP_FIRMWARE_OFFICIAL_STABLE (1)
|
||||||
#define TOTP_FIRMWARE_OFFICIAL_DEV 2
|
#define TOTP_FIRMWARE_OFFICIAL_DEV (2)
|
||||||
#define TOTP_FIRMWARE_XTREME 3
|
#define TOTP_FIRMWARE_XTREME (3)
|
||||||
// End of list
|
// End of list
|
||||||
|
|
||||||
// Target firmware to build for
|
// Target firmware to build for
|
||||||
|
#ifndef TOTP_TARGET_FIRMWARE
|
||||||
#define TOTP_TARGET_FIRMWARE TOTP_FIRMWARE_XTREME
|
#define TOTP_TARGET_FIRMWARE TOTP_FIRMWARE_XTREME
|
||||||
|
#endif
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ static const uint8_t dtable[] = {0x3e, 0x80, 0x80, 0x80, 0x3f, 0x34, 0x35, 0x36,
|
|||||||
0x80, 0x80, 0x80, 0x80, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
0x80, 0x80, 0x80, 0x80, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
|
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
|
||||||
0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33};
|
0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33};
|
||||||
// "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
||||||
static uint8_t get_dtable_value(uint8_t index) {
|
static uint8_t get_dtable_value(uint8_t index) {
|
||||||
return (index < 43 || index > 122) ? 0x80 : dtable[index - 43];
|
return (index < 43 || index > 122) ? 0x80 : dtable[index - 43];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("apps_data/authenticator")
|
#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("apps_data/authenticator")
|
||||||
#define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf"
|
#define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf"
|
||||||
#define CONFIG_FILE_BACKUP_PATH CONFIG_FILE_PATH ".backup"
|
#define CONFIG_FILE_BACKUP_BASE_PATH CONFIG_FILE_PATH ".backup"
|
||||||
#define CONFIG_FILE_TEMP_PATH CONFIG_FILE_PATH ".tmp"
|
#define CONFIG_FILE_TEMP_PATH CONFIG_FILE_PATH ".tmp"
|
||||||
#define CONFIG_FILE_ORIG_PATH CONFIG_FILE_PATH ".orig"
|
#define CONFIG_FILE_ORIG_PATH CONFIG_FILE_PATH ".orig"
|
||||||
#define CONFIG_FILE_PATH_PREVIOUS EXT_PATH("apps/Misc") "/totp.conf"
|
#define CONFIG_FILE_PATH_PREVIOUS EXT_PATH("apps/Misc") "/totp.conf"
|
||||||
@@ -39,6 +39,34 @@ static void totp_close_config_file(FlipperFormat* file) {
|
|||||||
flipper_format_free(file);
|
flipper_format_free(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Tries to take a config file backup
|
||||||
|
* @param storage storage record
|
||||||
|
* @return backup path if backup successfully taken; \c NULL otherwise
|
||||||
|
*/
|
||||||
|
static char* totp_config_file_backup_i(Storage* storage) {
|
||||||
|
uint8_t backup_path_size = sizeof(CONFIG_FILE_BACKUP_BASE_PATH) + 5;
|
||||||
|
char* backup_path = malloc(backup_path_size);
|
||||||
|
furi_check(backup_path != NULL);
|
||||||
|
memcpy(backup_path, CONFIG_FILE_BACKUP_BASE_PATH, sizeof(CONFIG_FILE_BACKUP_BASE_PATH));
|
||||||
|
uint16_t i = 1;
|
||||||
|
bool backup_file_exists;
|
||||||
|
while((backup_file_exists = storage_common_exists(storage, backup_path)) && i <= 9999) {
|
||||||
|
snprintf(backup_path, backup_path_size, CONFIG_FILE_BACKUP_BASE_PATH ".%" PRIu16, i);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(backup_file_exists ||
|
||||||
|
!storage_common_copy(storage, CONFIG_FILE_PATH, backup_path) == FSE_OK) {
|
||||||
|
FURI_LOG_E(LOGGING_TAG, "Unable to take a backup");
|
||||||
|
free(backup_path);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
FURI_LOG_I(LOGGING_TAG, "Took config file backup to %s", backup_path);
|
||||||
|
return backup_path;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Opens or creates TOTP application standard config file
|
* @brief Opens or creates TOTP application standard config file
|
||||||
* @param storage storage record to use
|
* @param storage storage record to use
|
||||||
@@ -250,6 +278,13 @@ static TotpConfigFileUpdateResult
|
|||||||
return update_result;
|
return update_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char* totp_config_file_backup() {
|
||||||
|
Storage* storage = totp_open_storage();
|
||||||
|
char* result = totp_config_file_backup_i(storage);
|
||||||
|
totp_close_storage();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
TotpConfigFileUpdateResult totp_config_file_save_new_token(const TokenInfo* token_info) {
|
TotpConfigFileUpdateResult totp_config_file_save_new_token(const TokenInfo* token_info) {
|
||||||
Storage* cfg_storage = totp_open_storage();
|
Storage* cfg_storage = totp_open_storage();
|
||||||
FlipperFormat* file;
|
FlipperFormat* file;
|
||||||
@@ -513,20 +548,16 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st
|
|||||||
CONFIG_FILE_ACTUAL_VERSION);
|
CONFIG_FILE_ACTUAL_VERSION);
|
||||||
totp_close_config_file(fff_data_file);
|
totp_close_config_file(fff_data_file);
|
||||||
|
|
||||||
if(storage_common_stat(storage, CONFIG_FILE_BACKUP_PATH, NULL) == FSE_OK) {
|
char* backup_path = totp_config_file_backup_i(storage);
|
||||||
storage_simply_remove(storage, CONFIG_FILE_BACKUP_PATH);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(storage_common_copy(storage, CONFIG_FILE_PATH, CONFIG_FILE_BACKUP_PATH) == FSE_OK) {
|
if(backup_path != NULL) {
|
||||||
FURI_LOG_I(LOGGING_TAG, "Took config file backup to %s", CONFIG_FILE_BACKUP_PATH);
|
|
||||||
if(totp_open_config_file(storage, &fff_data_file) != TotpConfigFileOpenSuccess) {
|
if(totp_open_config_file(storage, &fff_data_file) != TotpConfigFileOpenSuccess) {
|
||||||
result = TotpConfigFileOpenError;
|
result = TotpConfigFileOpenError;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
FlipperFormat* fff_backup_data_file = flipper_format_file_alloc(storage);
|
FlipperFormat* fff_backup_data_file = flipper_format_file_alloc(storage);
|
||||||
if(!flipper_format_file_open_existing(
|
if(!flipper_format_file_open_existing(fff_backup_data_file, backup_path)) {
|
||||||
fff_backup_data_file, CONFIG_FILE_BACKUP_PATH)) {
|
|
||||||
flipper_format_file_close(fff_backup_data_file);
|
flipper_format_file_close(fff_backup_data_file);
|
||||||
flipper_format_free(fff_backup_data_file);
|
flipper_format_free(fff_backup_data_file);
|
||||||
result = TotpConfigFileOpenError;
|
result = TotpConfigFileOpenError;
|
||||||
@@ -551,12 +582,12 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st
|
|||||||
flipper_format_file_close(fff_backup_data_file);
|
flipper_format_file_close(fff_backup_data_file);
|
||||||
flipper_format_free(fff_backup_data_file);
|
flipper_format_free(fff_backup_data_file);
|
||||||
flipper_format_rewind(fff_data_file);
|
flipper_format_rewind(fff_data_file);
|
||||||
|
free(backup_path);
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_E(
|
FURI_LOG_E(
|
||||||
LOGGING_TAG,
|
LOGGING_TAG,
|
||||||
"An error occurred during taking backup of %s into %s before migration",
|
"An error occurred during taking backup of %s before migration",
|
||||||
CONFIG_FILE_PATH,
|
CONFIG_FILE_PATH);
|
||||||
CONFIG_FILE_BACKUP_PATH);
|
|
||||||
result = TotpConfigFileOpenError;
|
result = TotpConfigFileOpenError;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,12 @@ enum TotpConfigFileUpdateResults {
|
|||||||
TotpConfigFileUpdateError
|
TotpConfigFileUpdateError
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Tries to take a config file backup
|
||||||
|
* @return backup path if backup successfully taken; \c NULL otherwise
|
||||||
|
*/
|
||||||
|
char* totp_config_file_backup();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Saves all the settings and tokens to an application config file
|
* @brief Saves all the settings and tokens to an application config file
|
||||||
* @param plugin_state application state
|
* @param plugin_state application state
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define CONFIG_FILE_HEADER "Flipper TOTP plugin config file"
|
#define CONFIG_FILE_HEADER "Flipper TOTP plugin config file"
|
||||||
#define CONFIG_FILE_ACTUAL_VERSION 4
|
#define CONFIG_FILE_ACTUAL_VERSION (4)
|
||||||
|
|
||||||
#define TOTP_CONFIG_KEY_TIMEZONE "Timezone"
|
#define TOTP_CONFIG_KEY_TIMEZONE "Timezone"
|
||||||
#define TOTP_CONFIG_KEY_TOKEN_NAME "TokenName"
|
#define TOTP_CONFIG_KEY_TOKEN_NAME "TokenName"
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
#include "../../types/common.h"
|
#include "../../types/common.h"
|
||||||
#include "memset_s.h"
|
#include "memset_s.h"
|
||||||
|
|
||||||
#define CRYPTO_KEY_SLOT 2
|
#define CRYPTO_KEY_SLOT (2)
|
||||||
#define CRYPTO_VERIFY_KEY "FFF_Crypto_pass"
|
#define CRYPTO_VERIFY_KEY "FFF_Crypto_pass"
|
||||||
#define CRYPTO_VERIFY_KEY_LENGTH 16
|
#define CRYPTO_VERIFY_KEY_LENGTH (16)
|
||||||
#define CRYPTO_ALIGNMENT_FACTOR 16
|
#define CRYPTO_ALIGNMENT_FACTOR (16)
|
||||||
|
|
||||||
uint8_t* totp_crypto_encrypt(
|
uint8_t* totp_crypto_encrypt(
|
||||||
const uint8_t* plain_data,
|
const uint8_t* plain_data,
|
||||||
|
|||||||
@@ -9,11 +9,7 @@
|
|||||||
#define _GLHMAC_CONCAT_(prefix, suffix) prefix##suffix
|
#define _GLHMAC_CONCAT_(prefix, suffix) prefix##suffix
|
||||||
#define _GLHMAC_CONCAT(prefix, suffix) _GLHMAC_CONCAT_(prefix, suffix)
|
#define _GLHMAC_CONCAT(prefix, suffix) _GLHMAC_CONCAT_(prefix, suffix)
|
||||||
|
|
||||||
#if GL_HMAC_NAME == 5
|
|
||||||
#define HMAC_ALG md5
|
|
||||||
#else
|
|
||||||
#define HMAC_ALG _GLHMAC_CONCAT(sha, GL_HMAC_NAME)
|
#define HMAC_ALG _GLHMAC_CONCAT(sha, GL_HMAC_NAME)
|
||||||
#endif
|
|
||||||
|
|
||||||
#define GL_HMAC_CTX _GLHMAC_CONCAT(HMAC_ALG, _ctx)
|
#define GL_HMAC_CTX _GLHMAC_CONCAT(HMAC_ALG, _ctx)
|
||||||
#define GL_HMAC_FN _GLHMAC_CONCAT(hmac_, HMAC_ALG)
|
#define GL_HMAC_FN _GLHMAC_CONCAT(hmac_, HMAC_ALG)
|
||||||
|
|||||||
6
applications/external/totp/totp_app.c
vendored
6
applications/external/totp/totp_app.c
vendored
@@ -20,7 +20,7 @@
|
|||||||
#include "services/crypto/crypto.h"
|
#include "services/crypto/crypto.h"
|
||||||
#include "cli/cli.h"
|
#include "cli/cli.h"
|
||||||
|
|
||||||
#define IDLE_TIMEOUT 60000
|
#define IDLE_TIMEOUT (60000)
|
||||||
|
|
||||||
static void render_callback(Canvas* const canvas, void* ctx) {
|
static void render_callback(Canvas* const canvas, void* ctx) {
|
||||||
furi_assert(ctx);
|
furi_assert(ctx);
|
||||||
@@ -96,6 +96,7 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) {
|
|||||||
plugin_state->gui = furi_record_open(RECORD_GUI);
|
plugin_state->gui = furi_record_open(RECORD_GUI);
|
||||||
plugin_state->notification_app = furi_record_open(RECORD_NOTIFICATION);
|
plugin_state->notification_app = furi_record_open(RECORD_NOTIFICATION);
|
||||||
plugin_state->dialogs_app = furi_record_open(RECORD_DIALOGS);
|
plugin_state->dialogs_app = furi_record_open(RECORD_DIALOGS);
|
||||||
|
memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE);
|
||||||
|
|
||||||
if(totp_config_file_load_base(plugin_state) != TotpConfigFileOpenSuccess) {
|
if(totp_config_file_load_base(plugin_state) != TotpConfigFileOpenSuccess) {
|
||||||
totp_dialogs_config_loading_error(plugin_state);
|
totp_dialogs_config_loading_error(plugin_state);
|
||||||
@@ -161,7 +162,7 @@ int32_t totp_app() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TotpCliContext* cli_context = totp_cli_register_command_handler(plugin_state, event_queue);
|
TotpCliContext* cli_context = totp_cli_register_command_handler(plugin_state, event_queue);
|
||||||
totp_scene_director_init_scenes(plugin_state);
|
|
||||||
if(!totp_activate_initial_scene(plugin_state)) {
|
if(!totp_activate_initial_scene(plugin_state)) {
|
||||||
FURI_LOG_E(LOGGING_TAG, "An error ocurred during activating initial scene\r\n");
|
FURI_LOG_E(LOGGING_TAG, "An error ocurred during activating initial scene\r\n");
|
||||||
totp_plugin_state_free(plugin_state);
|
totp_plugin_state_free(plugin_state);
|
||||||
@@ -206,7 +207,6 @@ int32_t totp_app() {
|
|||||||
|
|
||||||
totp_cli_unregister_command_handler(cli_context);
|
totp_cli_unregister_command_handler(cli_context);
|
||||||
totp_scene_director_deactivate_active_scene(plugin_state);
|
totp_scene_director_deactivate_active_scene(plugin_state);
|
||||||
totp_scene_director_dispose(plugin_state);
|
|
||||||
|
|
||||||
view_port_enabled_set(view_port, false);
|
view_port_enabled_set(view_port, false);
|
||||||
gui_remove_view_port(plugin_state->gui, view_port);
|
gui_remove_view_port(plugin_state->gui, view_port);
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
#include "../workers/bt_type_code/bt_type_code.h"
|
#include "../workers/bt_type_code/bt_type_code.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define TOTP_IV_SIZE 16
|
#define TOTP_IV_SIZE (16)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Application state structure
|
* @brief Application state structure
|
||||||
|
|||||||
17
applications/external/totp/types/token_info.h
vendored
17
applications/external/totp/types/token_info.h
vendored
@@ -4,13 +4,13 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <furi/furi.h>
|
#include <furi/furi.h>
|
||||||
|
|
||||||
#define TOTP_TOKEN_DURATION_DEFAULT 30
|
#define TOTP_TOKEN_DURATION_DEFAULT (30)
|
||||||
|
|
||||||
#define TOTP_TOKEN_ALGO_SHA1_NAME "sha1"
|
#define TOTP_TOKEN_ALGO_SHA1_NAME "sha1"
|
||||||
#define TOTP_TOKEN_ALGO_STEAM_NAME "steam"
|
#define TOTP_TOKEN_ALGO_STEAM_NAME "steam"
|
||||||
#define TOTP_TOKEN_ALGO_SHA256_NAME "sha256"
|
#define TOTP_TOKEN_ALGO_SHA256_NAME "sha256"
|
||||||
#define TOTP_TOKEN_ALGO_SHA512_NAME "sha512"
|
#define TOTP_TOKEN_ALGO_SHA512_NAME "sha512"
|
||||||
#define TOTP_TOKEN_MAX_LENGTH 255
|
#define TOTP_TOKEN_MAX_LENGTH (255)
|
||||||
|
|
||||||
#define PLAIN_TOKEN_ENCODING_BASE32_NAME "base32"
|
#define PLAIN_TOKEN_ENCODING_BASE32_NAME "base32"
|
||||||
#define PLAIN_TOKEN_ENCODING_BASE64_NAME "base64"
|
#define PLAIN_TOKEN_ENCODING_BASE64_NAME "base64"
|
||||||
@@ -95,12 +95,23 @@ enum TokenAutomationFeatures {
|
|||||||
TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER = 0b100
|
TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER = 0b100
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Plain token secret encodings.
|
||||||
|
*/
|
||||||
enum PlainTokenSecretEncodings {
|
enum PlainTokenSecretEncodings {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Base32 encoding
|
||||||
|
*/
|
||||||
PLAIN_TOKEN_ENCODING_BASE32 = 0,
|
PLAIN_TOKEN_ENCODING_BASE32 = 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Base64 encoding
|
||||||
|
*/
|
||||||
PLAIN_TOKEN_ENCODING_BASE64 = 1
|
PLAIN_TOKEN_ENCODING_BASE64 = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TOTP_TOKEN_DIGITS_MAX_COUNT 8
|
#define TOTP_TOKEN_DIGITS_MAX_COUNT (8)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief TOTP token information
|
* @brief TOTP token information
|
||||||
|
|||||||
4
applications/external/totp/ui/constants.h
vendored
4
applications/external/totp/ui/constants.h
vendored
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define SCREEN_WIDTH 128
|
#define SCREEN_WIDTH (128)
|
||||||
#define SCREEN_HEIGHT 64
|
#define SCREEN_HEIGHT (64)
|
||||||
#define SCREEN_WIDTH_CENTER (SCREEN_WIDTH >> 1)
|
#define SCREEN_WIDTH_CENTER (SCREEN_WIDTH >> 1)
|
||||||
#define SCREEN_HEIGHT_CENTER (SCREEN_HEIGHT >> 1)
|
#define SCREEN_HEIGHT_CENTER (SCREEN_HEIGHT >> 1)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "mode_nine.h"
|
#include "mode-nine.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
/* GENERATED BY https://github.com/pavius/the-dot-factory */
|
/* GENERATED BY https://github.com/pavius/the-dot-factory */
|
||||||
|
|
||||||
@@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
/* GENERATED BY https://github.com/pavius/the-dot-factory */
|
/* GENERATED BY https://github.com/pavius/the-dot-factory */
|
||||||
|
|
||||||
#include "../font_info.h"
|
#include "../font-info.h"
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/* Font data for ModeNine 15pt */
|
/* Font data for ModeNine 15pt */
|
||||||
extern const FONT_INFO modeNine_15ptFontInfo;
|
extern const FONT_INFO modeNine_15ptFontInfo;
|
||||||
16
applications/external/totp/ui/scene_director.c
vendored
16
applications/external/totp/ui/scene_director.c
vendored
@@ -62,14 +62,6 @@ void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void totp_scene_director_init_scenes(PluginState* const plugin_state) {
|
|
||||||
totp_scene_authenticate_init(plugin_state);
|
|
||||||
totp_scene_generate_token_init(plugin_state);
|
|
||||||
totp_scene_add_new_token_init(plugin_state);
|
|
||||||
totp_scene_token_menu_init(plugin_state);
|
|
||||||
totp_scene_app_settings_init(plugin_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_state) {
|
void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_state) {
|
||||||
switch(plugin_state->current_scene) {
|
switch(plugin_state->current_scene) {
|
||||||
case TotpSceneGenerateToken:
|
case TotpSceneGenerateToken:
|
||||||
@@ -94,14 +86,6 @@ void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void totp_scene_director_dispose(const PluginState* const plugin_state) {
|
|
||||||
totp_scene_generate_token_free(plugin_state);
|
|
||||||
totp_scene_authenticate_free(plugin_state);
|
|
||||||
totp_scene_add_new_token_free(plugin_state);
|
|
||||||
totp_scene_token_menu_free(plugin_state);
|
|
||||||
totp_scene_app_settings_free(plugin_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* const plugin_state) {
|
bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* const plugin_state) {
|
||||||
bool processing = true;
|
bool processing = true;
|
||||||
switch(plugin_state->current_scene) {
|
switch(plugin_state->current_scene) {
|
||||||
|
|||||||
12
applications/external/totp/ui/scene_director.h
vendored
12
applications/external/totp/ui/scene_director.h
vendored
@@ -22,12 +22,6 @@ void totp_scene_director_activate_scene(
|
|||||||
*/
|
*/
|
||||||
void totp_scene_director_deactivate_active_scene(PluginState* const plugin_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
|
* @brief Renders current scene
|
||||||
* @param canvas canvas to render at
|
* @param canvas canvas to render at
|
||||||
@@ -35,12 +29,6 @@ void totp_scene_director_init_scenes(PluginState* const plugin_state);
|
|||||||
*/
|
*/
|
||||||
void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_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
|
* @brief Handles application event for the current scene
|
||||||
* @param event event to be handled
|
* @param event event to be handled
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
#include "../../../types/plugin_state.h"
|
#include "../../../types/plugin_state.h"
|
||||||
#include "../../../types/plugin_event.h"
|
#include "../../../types/plugin_event.h"
|
||||||
|
|
||||||
|
#define INPUT_BUFFER_SIZE (255)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char* user_input;
|
char* user_input;
|
||||||
size_t user_input_length;
|
size_t user_input_length;
|
||||||
@@ -20,8 +22,6 @@ typedef struct {
|
|||||||
void* callback_data;
|
void* callback_data;
|
||||||
} InputTextSceneContext;
|
} InputTextSceneContext;
|
||||||
|
|
||||||
#define INPUT_BUFFER_SIZE 255
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
TextInput* text_input;
|
TextInput* text_input;
|
||||||
View* text_input_view;
|
View* text_input_view;
|
||||||
|
|||||||
@@ -44,10 +44,6 @@ typedef struct {
|
|||||||
FuriString* duration_text;
|
FuriString* duration_text;
|
||||||
} SceneState;
|
} SceneState;
|
||||||
|
|
||||||
void totp_scene_add_new_token_init(const PluginState* plugin_state) {
|
|
||||||
UNUSED(plugin_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void on_token_name_user_comitted(InputTextSceneCallbackResult* result) {
|
static void on_token_name_user_comitted(InputTextSceneCallbackResult* result) {
|
||||||
SceneState* scene_state = result->callback_data;
|
SceneState* scene_state = result->callback_data;
|
||||||
free(scene_state->token_name);
|
free(scene_state->token_name);
|
||||||
@@ -354,7 +350,3 @@ void totp_scene_add_new_token_deactivate(PluginState* plugin_state) {
|
|||||||
free(plugin_state->current_scene_state);
|
free(plugin_state->current_scene_state);
|
||||||
plugin_state->current_scene_state = NULL;
|
plugin_state->current_scene_state = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void totp_scene_add_new_token_free(const PluginState* plugin_state) {
|
|
||||||
UNUSED(plugin_state);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -10,11 +10,9 @@ typedef struct {
|
|||||||
uint16_t current_token_index;
|
uint16_t current_token_index;
|
||||||
} TokenAddEditSceneContext;
|
} TokenAddEditSceneContext;
|
||||||
|
|
||||||
void totp_scene_add_new_token_init(const PluginState* plugin_state);
|
|
||||||
void totp_scene_add_new_token_activate(
|
void totp_scene_add_new_token_activate(
|
||||||
PluginState* plugin_state,
|
PluginState* plugin_state,
|
||||||
const TokenAddEditSceneContext* context);
|
const TokenAddEditSceneContext* context);
|
||||||
void totp_scene_add_new_token_render(Canvas* const canvas, PluginState* plugin_state);
|
void totp_scene_add_new_token_render(Canvas* const canvas, PluginState* plugin_state);
|
||||||
bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState* plugin_state);
|
bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState* plugin_state);
|
||||||
void totp_scene_add_new_token_deactivate(PluginState* plugin_state);
|
void totp_scene_add_new_token_deactivate(PluginState* plugin_state);
|
||||||
void totp_scene_add_new_token_free(const PluginState* plugin_state);
|
|
||||||
|
|||||||
@@ -44,10 +44,6 @@ typedef struct {
|
|||||||
Control selected_control;
|
Control selected_control;
|
||||||
} SceneState;
|
} SceneState;
|
||||||
|
|
||||||
void totp_scene_app_settings_init(const PluginState* plugin_state) {
|
|
||||||
UNUSED(plugin_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void totp_scene_app_settings_activate(
|
void totp_scene_app_settings_activate(
|
||||||
PluginState* plugin_state,
|
PluginState* plugin_state,
|
||||||
const AppSettingsSceneContext* context) {
|
const AppSettingsSceneContext* context) {
|
||||||
@@ -332,7 +328,3 @@ void totp_scene_app_settings_deactivate(PluginState* plugin_state) {
|
|||||||
free(plugin_state->current_scene_state);
|
free(plugin_state->current_scene_state);
|
||||||
plugin_state->current_scene_state = NULL;
|
plugin_state->current_scene_state = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void totp_scene_app_settings_free(const PluginState* plugin_state) {
|
|
||||||
UNUSED(plugin_state);
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,6 @@ typedef struct {
|
|||||||
uint16_t current_token_index;
|
uint16_t current_token_index;
|
||||||
} AppSettingsSceneContext;
|
} AppSettingsSceneContext;
|
||||||
|
|
||||||
void totp_scene_app_settings_init(const PluginState* plugin_state);
|
|
||||||
void totp_scene_app_settings_activate(
|
void totp_scene_app_settings_activate(
|
||||||
PluginState* plugin_state,
|
PluginState* plugin_state,
|
||||||
const AppSettingsSceneContext* context);
|
const AppSettingsSceneContext* context);
|
||||||
@@ -17,4 +16,3 @@ bool totp_scene_app_settings_handle_event(
|
|||||||
const PluginEvent* const event,
|
const PluginEvent* const event,
|
||||||
PluginState* plugin_state);
|
PluginState* plugin_state);
|
||||||
void totp_scene_app_settings_deactivate(PluginState* plugin_state);
|
void totp_scene_app_settings_deactivate(PluginState* plugin_state);
|
||||||
void totp_scene_app_settings_free(const PluginState* plugin_state);
|
|
||||||
@@ -18,10 +18,6 @@ typedef struct {
|
|||||||
uint8_t code_length;
|
uint8_t code_length;
|
||||||
} SceneState;
|
} SceneState;
|
||||||
|
|
||||||
void totp_scene_authenticate_init(PluginState* plugin_state) {
|
|
||||||
memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void totp_scene_authenticate_activate(PluginState* plugin_state) {
|
void totp_scene_authenticate_activate(PluginState* plugin_state) {
|
||||||
SceneState* scene_state = malloc(sizeof(SceneState));
|
SceneState* scene_state = malloc(sizeof(SceneState));
|
||||||
furi_check(scene_state != NULL);
|
furi_check(scene_state != NULL);
|
||||||
@@ -162,7 +158,3 @@ void totp_scene_authenticate_deactivate(PluginState* plugin_state) {
|
|||||||
free(plugin_state->current_scene_state);
|
free(plugin_state->current_scene_state);
|
||||||
plugin_state->current_scene_state = NULL;
|
plugin_state->current_scene_state = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void totp_scene_authenticate_free(const PluginState* plugin_state) {
|
|
||||||
UNUSED(plugin_state);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,11 +4,9 @@
|
|||||||
#include "../../../types/plugin_state.h"
|
#include "../../../types/plugin_state.h"
|
||||||
#include "../../../types/plugin_event.h"
|
#include "../../../types/plugin_event.h"
|
||||||
|
|
||||||
void totp_scene_authenticate_init(PluginState* plugin_state);
|
|
||||||
void totp_scene_authenticate_activate(PluginState* plugin_state);
|
void totp_scene_authenticate_activate(PluginState* plugin_state);
|
||||||
void totp_scene_authenticate_render(Canvas* const canvas, PluginState* plugin_state);
|
void totp_scene_authenticate_render(Canvas* const canvas, PluginState* plugin_state);
|
||||||
bool totp_scene_authenticate_handle_event(
|
bool totp_scene_authenticate_handle_event(
|
||||||
const PluginEvent* const event,
|
const PluginEvent* const event,
|
||||||
PluginState* plugin_state);
|
PluginState* plugin_state);
|
||||||
void totp_scene_authenticate_deactivate(PluginState* plugin_state);
|
void totp_scene_authenticate_deactivate(PluginState* plugin_state);
|
||||||
void totp_scene_authenticate_free(const PluginState* plugin_state);
|
|
||||||
|
|||||||
@@ -19,11 +19,11 @@
|
|||||||
#ifdef TOTP_BADBT_TYPE_ENABLED
|
#ifdef TOTP_BADBT_TYPE_ENABLED
|
||||||
#include "../../../workers/bt_type_code/bt_type_code.h"
|
#include "../../../workers/bt_type_code/bt_type_code.h"
|
||||||
#endif
|
#endif
|
||||||
#include "../../fonts/mode-nine/mode_nine.h"
|
#include "../../fonts/mode-nine/mode-nine.h"
|
||||||
|
|
||||||
|
#define PROGRESS_BAR_MARGIN (3)
|
||||||
|
#define PROGRESS_BAR_HEIGHT (4)
|
||||||
static const char* STEAM_ALGO_ALPHABET = "23456789BCDFGHJKMNPQRTVWXY";
|
static const char* STEAM_ALGO_ALPHABET = "23456789BCDFGHJKMNPQRTVWXY";
|
||||||
static const uint8_t PROGRESS_BAR_MARGIN = 3;
|
|
||||||
static const uint8_t PROGRESS_BAR_HEIGHT = 4;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint16_t current_token_index;
|
uint16_t current_token_index;
|
||||||
@@ -177,6 +177,7 @@ static void draw_totp_code(Canvas* const canvas, const SceneState* const scene_s
|
|||||||
uint8_t char_width = modeNine_15ptFontInfo.charInfo[0].width;
|
uint8_t char_width = modeNine_15ptFontInfo.charInfo[0].width;
|
||||||
uint8_t total_length = code_length * (char_width + modeNine_15ptFontInfo.spacePixels);
|
uint8_t total_length = code_length * (char_width + modeNine_15ptFontInfo.spacePixels);
|
||||||
uint8_t offset_x = (SCREEN_WIDTH - total_length) >> 1;
|
uint8_t offset_x = (SCREEN_WIDTH - total_length) >> 1;
|
||||||
|
uint8_t offset_x_inc = char_width + modeNine_15ptFontInfo.spacePixels;
|
||||||
uint8_t offset_y = SCREEN_HEIGHT_CENTER - (modeNine_15ptFontInfo.height >> 1);
|
uint8_t offset_y = SCREEN_HEIGHT_CENTER - (modeNine_15ptFontInfo.height >> 1);
|
||||||
for(uint8_t i = 0; i < code_length; i++) {
|
for(uint8_t i = 0; i < code_length; i++) {
|
||||||
char ch = scene_state->last_code[i];
|
char ch = scene_state->last_code[i];
|
||||||
@@ -189,14 +190,10 @@ static void draw_totp_code(Canvas* const canvas, const SceneState* const scene_s
|
|||||||
modeNine_15ptFontInfo.height,
|
modeNine_15ptFontInfo.height,
|
||||||
&modeNine_15ptFontInfo.data[modeNine_15ptFontInfo.charInfo[char_index].offset]);
|
&modeNine_15ptFontInfo.data[modeNine_15ptFontInfo.charInfo[char_index].offset]);
|
||||||
|
|
||||||
offset_x += char_width + modeNine_15ptFontInfo.spacePixels;
|
offset_x += offset_x_inc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void totp_scene_generate_token_init(const PluginState* plugin_state) {
|
|
||||||
UNUSED(plugin_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void totp_scene_generate_token_activate(
|
void totp_scene_generate_token_activate(
|
||||||
PluginState* plugin_state,
|
PluginState* plugin_state,
|
||||||
const GenerateTokenSceneContext* context) {
|
const GenerateTokenSceneContext* context) {
|
||||||
@@ -518,7 +515,3 @@ void totp_scene_generate_token_deactivate(PluginState* plugin_state) {
|
|||||||
free(scene_state);
|
free(scene_state);
|
||||||
plugin_state->current_scene_state = NULL;
|
plugin_state->current_scene_state = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void totp_scene_generate_token_free(const PluginState* plugin_state) {
|
|
||||||
UNUSED(plugin_state);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ typedef struct {
|
|||||||
uint16_t current_token_index;
|
uint16_t current_token_index;
|
||||||
} GenerateTokenSceneContext;
|
} GenerateTokenSceneContext;
|
||||||
|
|
||||||
void totp_scene_generate_token_init(const PluginState* plugin_state);
|
|
||||||
void totp_scene_generate_token_activate(
|
void totp_scene_generate_token_activate(
|
||||||
PluginState* plugin_state,
|
PluginState* plugin_state,
|
||||||
const GenerateTokenSceneContext* context);
|
const GenerateTokenSceneContext* context);
|
||||||
@@ -17,4 +16,3 @@ bool totp_scene_generate_token_handle_event(
|
|||||||
const PluginEvent* const event,
|
const PluginEvent* const event,
|
||||||
PluginState* plugin_state);
|
PluginState* plugin_state);
|
||||||
void totp_scene_generate_token_deactivate(PluginState* plugin_state);
|
void totp_scene_generate_token_deactivate(PluginState* plugin_state);
|
||||||
void totp_scene_generate_token_free(const PluginState* plugin_state);
|
|
||||||
|
|||||||
@@ -24,10 +24,6 @@ typedef struct {
|
|||||||
TotpNullable_uint16_t current_token_index;
|
TotpNullable_uint16_t current_token_index;
|
||||||
} SceneState;
|
} SceneState;
|
||||||
|
|
||||||
void totp_scene_token_menu_init(const PluginState* plugin_state) {
|
|
||||||
UNUSED(plugin_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void totp_scene_token_menu_activate(
|
void totp_scene_token_menu_activate(
|
||||||
PluginState* plugin_state,
|
PluginState* plugin_state,
|
||||||
const TokenMenuSceneContext* context) {
|
const TokenMenuSceneContext* context) {
|
||||||
@@ -204,7 +200,3 @@ void totp_scene_token_menu_deactivate(PluginState* plugin_state) {
|
|||||||
free(plugin_state->current_scene_state);
|
free(plugin_state->current_scene_state);
|
||||||
plugin_state->current_scene_state = NULL;
|
plugin_state->current_scene_state = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void totp_scene_token_menu_free(const PluginState* plugin_state) {
|
|
||||||
UNUSED(plugin_state);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,11 +8,9 @@ typedef struct {
|
|||||||
uint16_t current_token_index;
|
uint16_t current_token_index;
|
||||||
} TokenMenuSceneContext;
|
} TokenMenuSceneContext;
|
||||||
|
|
||||||
void totp_scene_token_menu_init(const PluginState* plugin_state);
|
|
||||||
void totp_scene_token_menu_activate(
|
void totp_scene_token_menu_activate(
|
||||||
PluginState* plugin_state,
|
PluginState* plugin_state,
|
||||||
const TokenMenuSceneContext* context);
|
const TokenMenuSceneContext* context);
|
||||||
void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_state);
|
void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_state);
|
||||||
bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginState* plugin_state);
|
bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginState* plugin_state);
|
||||||
void totp_scene_token_menu_deactivate(PluginState* plugin_state);
|
void totp_scene_token_menu_deactivate(PluginState* plugin_state);
|
||||||
void totp_scene_token_menu_free(const PluginState* plugin_state);
|
|
||||||
|
|||||||
4
applications/external/totp/ui/ui_controls.c
vendored
4
applications/external/totp/ui/ui_controls.c
vendored
@@ -2,8 +2,8 @@
|
|||||||
#include <totp_icons.h>
|
#include <totp_icons.h>
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
|
|
||||||
#define TEXT_BOX_HEIGHT 13
|
#define TEXT_BOX_HEIGHT (13)
|
||||||
#define TEXT_BOX_MARGIN 4
|
#define TEXT_BOX_MARGIN (4)
|
||||||
|
|
||||||
void ui_control_text_box_render(
|
void ui_control_text_box_render(
|
||||||
Canvas* const canvas,
|
Canvas* const canvas,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ typedef struct {
|
|||||||
bool is_connected;
|
bool is_connected;
|
||||||
#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME
|
#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME
|
||||||
uint8_t bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN];
|
uint8_t bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN];
|
||||||
char previous_bt_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN + 1];
|
char previous_bt_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN];
|
||||||
uint8_t previous_bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN];
|
uint8_t previous_bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN];
|
||||||
#endif
|
#endif
|
||||||
} TotpBtTypeCodeWorkerContext;
|
} TotpBtTypeCodeWorkerContext;
|
||||||
|
|||||||
Reference in New Issue
Block a user