mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-05-12 11:18:11 -07:00
Decompose utils from duox-related code
This commit is contained in:
+12
-195
@@ -18,12 +18,9 @@
|
||||
|
||||
#include "crypto/duoxcrypto.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <mbedtls/asn1.h>
|
||||
#include <mbedtls/ecdsa.h>
|
||||
#include <mbedtls/ecp.h>
|
||||
@@ -40,6 +37,7 @@
|
||||
#include "x509_crt.h"
|
||||
|
||||
#define DUOX_CERTIFICATE_ANCHOR_INPUT_LEN 8192
|
||||
#define DUOX_CERTIFICATE_ANCHOR_SEARCH_DEPTH 4
|
||||
|
||||
static int duox_cert_info_from_x509_crt(const mbedtls_x509_crt *cert, duox_cert_info_t *out);
|
||||
|
||||
@@ -55,189 +53,6 @@ const char *duox_certificate_format_name(duox_certificate_format_t format) {
|
||||
}
|
||||
}
|
||||
|
||||
const char *duox_cert_info_format_name(const duox_cert_info_t *cert) {
|
||||
if (cert == NULL) {
|
||||
return duox_certificate_format_name(DUOX_CERTIFICATE_FORMAT_UNKNOWN);
|
||||
}
|
||||
return duox_certificate_format_name(cert->format);
|
||||
}
|
||||
|
||||
static void duox_trim_ascii_inplace(char *text) {
|
||||
if (text == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t start = 0;
|
||||
size_t len = strlen(text);
|
||||
while (start < len && isspace((unsigned char)text[start])) {
|
||||
start++;
|
||||
}
|
||||
while (len > start && isspace((unsigned char)text[len - 1])) {
|
||||
len--;
|
||||
}
|
||||
|
||||
if (start > 0) {
|
||||
memmove(text, text + start, len - start);
|
||||
}
|
||||
text[len - start] = '\0';
|
||||
}
|
||||
|
||||
static int duox_copy_without_whitespace(const char *src, char *dst, size_t dst_size, size_t *dst_len) {
|
||||
if (src == NULL || dst == NULL || dst_len == NULL || dst_size == 0) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
size_t out = 0;
|
||||
for (size_t i = 0; src[i] != '\0'; i++) {
|
||||
if (isspace((unsigned char)src[i])) {
|
||||
continue;
|
||||
}
|
||||
if ((out + 1) >= dst_size) {
|
||||
return PM3_EOVFLOW;
|
||||
}
|
||||
dst[out++] = src[i];
|
||||
}
|
||||
dst[out] = '\0';
|
||||
*dst_len = out;
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static bool duox_path_is_directory(const char *path) {
|
||||
if (path == NULL) {
|
||||
return false;
|
||||
}
|
||||
struct stat st;
|
||||
if (stat(path, &st) != 0) {
|
||||
return false;
|
||||
}
|
||||
return S_ISDIR(st.st_mode) != 0;
|
||||
}
|
||||
|
||||
static bool duox_path_is_regular_file(const char *path) {
|
||||
if (path == NULL) {
|
||||
return false;
|
||||
}
|
||||
struct stat st;
|
||||
if (stat(path, &st) != 0) {
|
||||
return false;
|
||||
}
|
||||
return S_ISREG(st.st_mode) != 0;
|
||||
}
|
||||
|
||||
static const char *duox_path_basename(const char *path) {
|
||||
if (path == NULL) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const char *base = strrchr(path, '/');
|
||||
const char *base_win = strrchr(path, '\\');
|
||||
if (base == NULL || (base_win != NULL && base_win > base)) {
|
||||
base = base_win;
|
||||
}
|
||||
return (base == NULL) ? path : (base + 1);
|
||||
}
|
||||
|
||||
static void duox_path_basename_without_ext(const char *path, char *out, size_t out_len) {
|
||||
if (out == NULL || out_len == 0) {
|
||||
return;
|
||||
}
|
||||
out[0] = '\0';
|
||||
|
||||
const char *base = duox_path_basename(path);
|
||||
if (base[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(out, out_len, "%s", base);
|
||||
char *dot = strrchr(out, '.');
|
||||
if (dot != NULL && dot != out) {
|
||||
*dot = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
static int duox_qsort_path_cmp(const void *a, const void *b) {
|
||||
const char *pa = (const char *)a;
|
||||
const char *pb = (const char *)b;
|
||||
return strcmp(pa, pb);
|
||||
}
|
||||
|
||||
static int duox_collect_certificate_anchor_paths_recursive(const char *dirpath,
|
||||
char paths[][DUOX_CERTIFICATE_ANCHOR_PATH_LEN],
|
||||
size_t max_paths, size_t *count) {
|
||||
if (dirpath == NULL || paths == NULL || count == NULL) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
DIR *dir = opendir(dirpath);
|
||||
if (dir == NULL) {
|
||||
return PM3_EFILE;
|
||||
}
|
||||
|
||||
struct dirent *entry = NULL;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0 || entry->d_name[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
|
||||
char fullpath[DUOX_CERTIFICATE_ANCHOR_PATH_LEN] = {0};
|
||||
if (snprintf(fullpath, sizeof(fullpath), "%s/%s", dirpath, entry->d_name) >= (int)sizeof(fullpath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (duox_path_is_directory(fullpath)) {
|
||||
int res = duox_collect_certificate_anchor_paths_recursive(fullpath, paths, max_paths, count);
|
||||
if (res != PM3_SUCCESS) {
|
||||
closedir(dir);
|
||||
return res;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!duox_path_is_regular_file(fullpath)) {
|
||||
continue;
|
||||
}
|
||||
if (*count >= max_paths) {
|
||||
closedir(dir);
|
||||
return PM3_EOVFLOW;
|
||||
}
|
||||
|
||||
snprintf(paths[*count], DUOX_CERTIFICATE_ANCHOR_PATH_LEN, "%s", fullpath);
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int duox_collect_certificate_anchor_paths(const char *anchor_store_dir,
|
||||
char paths[][DUOX_CERTIFICATE_ANCHOR_PATH_LEN],
|
||||
size_t max_paths, size_t *count) {
|
||||
if (anchor_store_dir == NULL || anchor_store_dir[0] == '\0' || paths == NULL || count == NULL) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
char *rootdir = NULL;
|
||||
int res = searchFile(&rootdir, RESOURCES_SUBDIR, anchor_store_dir, "", true);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (!duox_path_is_directory(rootdir)) {
|
||||
free(rootdir);
|
||||
return PM3_EFILE;
|
||||
}
|
||||
|
||||
*count = 0;
|
||||
res = duox_collect_certificate_anchor_paths_recursive(rootdir, paths, max_paths, count);
|
||||
free(rootdir);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
|
||||
qsort(paths, *count, sizeof(paths[0]), duox_qsort_path_cmp);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
int duox_certificate_anchor_public_key(const duox_certificate_anchor_t *anchor,
|
||||
mbedtls_ecp_group_id *curveid,
|
||||
const uint8_t **pubkey, size_t *pubkey_len) {
|
||||
@@ -299,7 +114,7 @@ static int duox_load_x509_certificate_input(const char *input, mbedtls_x509_crt
|
||||
return PM3_EOVFLOW;
|
||||
}
|
||||
memcpy(normalized, input, input_len + 1);
|
||||
duox_trim_ascii_inplace(normalized);
|
||||
str_trim_ascii_inplace(normalized);
|
||||
if (normalized[0] == '\0') {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
@@ -319,7 +134,7 @@ static int duox_load_x509_certificate_input(const char *input, mbedtls_x509_crt
|
||||
|
||||
char compact[DUOX_CERTIFICATE_ANCHOR_INPUT_LEN] = {0};
|
||||
size_t compact_len = 0;
|
||||
if (duox_copy_without_whitespace(normalized, compact, sizeof(compact), &compact_len) != PM3_SUCCESS || compact_len == 0) {
|
||||
if (str_copy_without_whitespace(normalized, compact, sizeof(compact), &compact_len) != PM3_SUCCESS || compact_len == 0) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
@@ -425,7 +240,7 @@ static int duox_load_certificate_anchor_from_file_path(const char *filepath, duo
|
||||
}
|
||||
|
||||
char filename_anchor_name[DUOX_CERTIFICATE_ANCHOR_NAME_LEN] = {0};
|
||||
duox_path_basename_without_ext(filepath, filename_anchor_name, sizeof(filename_anchor_name));
|
||||
path_basename_without_ext(filepath, filename_anchor_name, sizeof(filename_anchor_name));
|
||||
if (filename_anchor_name[0] == '\0') {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
@@ -446,14 +261,15 @@ static int duox_load_named_certificate_anchor_from_store(const char *token, cons
|
||||
|
||||
char paths[DUOX_CERTIFICATE_ANCHOR_MAX_PATHS][DUOX_CERTIFICATE_ANCHOR_PATH_LEN] = {{0}};
|
||||
size_t path_count = 0;
|
||||
int res = duox_collect_certificate_anchor_paths(anchor_store_dir, paths, ARRAYLEN(paths), &path_count);
|
||||
int res = collect_resource_file_paths(anchor_store_dir, (char *)paths, sizeof(paths[0]), ARRAYLEN(paths), &path_count,
|
||||
false, DUOX_CERTIFICATE_ANCHOR_SEARCH_DEPTH);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < path_count; i++) {
|
||||
char filename_anchor_name[DUOX_CERTIFICATE_ANCHOR_NAME_LEN] = {0};
|
||||
duox_path_basename_without_ext(paths[i], filename_anchor_name, sizeof(filename_anchor_name));
|
||||
path_basename_without_ext(paths[i], filename_anchor_name, sizeof(filename_anchor_name));
|
||||
if (filename_anchor_name[0] == '\0') {
|
||||
continue;
|
||||
}
|
||||
@@ -466,7 +282,7 @@ static int duox_load_named_certificate_anchor_from_store(const char *token, cons
|
||||
const char *matched_path = NULL;
|
||||
for (size_t i = 0; i < path_count; i++) {
|
||||
char filename_anchor_name[DUOX_CERTIFICATE_ANCHOR_NAME_LEN] = {0};
|
||||
duox_path_basename_without_ext(paths[i], filename_anchor_name, sizeof(filename_anchor_name));
|
||||
path_basename_without_ext(paths[i], filename_anchor_name, sizeof(filename_anchor_name));
|
||||
if (filename_anchor_name[0] == '\0' || !str_startswith_case_insensitive(filename_anchor_name, token)) {
|
||||
continue;
|
||||
}
|
||||
@@ -491,7 +307,7 @@ int duox_load_certificate_anchor_from_input(const char *input, const char *ancho
|
||||
|
||||
char normalized[DUOX_CERTIFICATE_ANCHOR_INPUT_LEN] = {0};
|
||||
snprintf(normalized, sizeof(normalized), "%s", input);
|
||||
duox_trim_ascii_inplace(normalized);
|
||||
str_trim_ascii_inplace(normalized);
|
||||
if (normalized[0] == '\0') {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
@@ -499,7 +315,7 @@ int duox_load_certificate_anchor_from_input(const char *input, const char *ancho
|
||||
char *resolved_path = NULL;
|
||||
if (searchFile(&resolved_path, RESOURCES_SUBDIR, normalized, "", true) == PM3_SUCCESS) {
|
||||
int res = PM3_EINVARG;
|
||||
if (duox_path_is_regular_file(resolved_path)) {
|
||||
if (path_is_regular_file(resolved_path)) {
|
||||
res = duox_load_certificate_anchor_from_file_path(resolved_path, anchor);
|
||||
}
|
||||
free(resolved_path);
|
||||
@@ -532,7 +348,8 @@ int duox_load_certificate_anchors_from_store(const char *anchor_store_dir,
|
||||
|
||||
char paths[DUOX_CERTIFICATE_ANCHOR_MAX_PATHS][DUOX_CERTIFICATE_ANCHOR_PATH_LEN] = {{0}};
|
||||
size_t path_count = 0;
|
||||
int res = duox_collect_certificate_anchor_paths(anchor_store_dir, paths, ARRAYLEN(paths), &path_count);
|
||||
int res = collect_resource_file_paths(anchor_store_dir, (char *)paths, sizeof(paths[0]), ARRAYLEN(paths), &path_count,
|
||||
false, DUOX_CERTIFICATE_ANCHOR_SEARCH_DEPTH);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,6 @@ typedef struct {
|
||||
} duox_certificate_anchor_t;
|
||||
|
||||
const char *duox_certificate_format_name(duox_certificate_format_t format);
|
||||
const char *duox_cert_info_format_name(const duox_cert_info_t *cert);
|
||||
int duox_certificate_anchor_public_key(const duox_certificate_anchor_t *anchor,
|
||||
mbedtls_ecp_group_id *curveid,
|
||||
const uint8_t **pubkey, size_t *pubkey_len);
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <mbedtls/asn1.h>
|
||||
#include <mbedtls/des.h>
|
||||
#include <mbedtls/aes.h>
|
||||
@@ -106,78 +105,6 @@ int pcrypto_rng_fill_oneshot(uint8_t *out, size_t out_len, const char *personali
|
||||
return res;
|
||||
}
|
||||
|
||||
static void pcrypto_trim_ascii_inplace(char *text) {
|
||||
if (text == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t start = 0;
|
||||
size_t len = strlen(text);
|
||||
while (start < len && isspace((unsigned char)text[start])) {
|
||||
start++;
|
||||
}
|
||||
while (len > start && isspace((unsigned char)text[len - 1])) {
|
||||
len--;
|
||||
}
|
||||
|
||||
if (start > 0) {
|
||||
memmove(text, text + start, len - start);
|
||||
}
|
||||
text[len - start] = '\0';
|
||||
}
|
||||
|
||||
static void pcrypto_unescape_newlines_inplace(char *text) {
|
||||
if (text == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t read_pos = 0;
|
||||
size_t write_pos = 0;
|
||||
size_t len = strlen(text);
|
||||
while (read_pos < len) {
|
||||
if (text[read_pos] == '\\' && (read_pos + 1) < len) {
|
||||
char esc = text[read_pos + 1];
|
||||
if (esc == 'n') {
|
||||
text[write_pos++] = '\n';
|
||||
read_pos += 2;
|
||||
continue;
|
||||
}
|
||||
if (esc == 'r') {
|
||||
text[write_pos++] = '\r';
|
||||
read_pos += 2;
|
||||
continue;
|
||||
}
|
||||
if (esc == 't') {
|
||||
text[write_pos++] = '\t';
|
||||
read_pos += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
text[write_pos++] = text[read_pos++];
|
||||
}
|
||||
text[write_pos] = '\0';
|
||||
}
|
||||
|
||||
static int pcrypto_copy_without_whitespace(const char *src, char *dst, size_t dst_size, size_t *dst_len) {
|
||||
if (src == NULL || dst == NULL || dst_len == NULL || dst_size == 0) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
size_t out = 0;
|
||||
for (size_t i = 0; src[i] != '\0'; i++) {
|
||||
if (isspace((unsigned char)src[i])) {
|
||||
continue;
|
||||
}
|
||||
if ((out + 1) >= dst_size) {
|
||||
return PM3_EOVFLOW;
|
||||
}
|
||||
dst[out++] = src[i];
|
||||
}
|
||||
dst[out] = '\0';
|
||||
*dst_len = out;
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int pcrypto_extract_priv_scalar_from_pk(const mbedtls_pk_context *pkctx,
|
||||
mbedtls_ecp_group_id curveid,
|
||||
uint8_t *out_priv, size_t out_priv_len) {
|
||||
@@ -265,7 +192,7 @@ static int pcrypto_parse_ec_private_base64(const char *input,
|
||||
|
||||
char compact[PCRYPTO_MAX_KEY_INPUT] = {0};
|
||||
size_t compact_len = 0;
|
||||
int res = pcrypto_copy_without_whitespace(input, compact, sizeof(compact), &compact_len);
|
||||
int res = str_copy_without_whitespace(input, compact, sizeof(compact), &compact_len);
|
||||
if (res != PM3_SUCCESS || compact_len == 0) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
@@ -399,7 +326,7 @@ static int pcrypto_parse_ec_private_text(const char *input, bool allow_file_path
|
||||
return PM3_EOVFLOW;
|
||||
}
|
||||
memcpy(normalized, input, input_len + 1);
|
||||
pcrypto_trim_ascii_inplace(normalized);
|
||||
str_trim_ascii_inplace(normalized);
|
||||
|
||||
if (normalized[0] == '\0') {
|
||||
return PM3_EINVARG;
|
||||
@@ -416,13 +343,13 @@ static int pcrypto_parse_ec_private_text(const char *input, bool allow_file_path
|
||||
|
||||
// Only unescape after path resolution fails, to avoid mutating valid paths
|
||||
// (for example Windows paths containing '\t', '\n' or '\r').
|
||||
pcrypto_unescape_newlines_inplace(normalized);
|
||||
str_unescape_newlines_inplace(normalized);
|
||||
|
||||
uint8_t decoded[PCRYPTO_MAX_KEY_INPUT] = {0};
|
||||
int decoded_len = -1;
|
||||
char compact[PCRYPTO_MAX_KEY_INPUT] = {0};
|
||||
size_t compact_len = 0;
|
||||
if (pcrypto_copy_without_whitespace(normalized, compact, sizeof(compact), &compact_len) == PM3_SUCCESS &&
|
||||
if (str_copy_without_whitespace(normalized, compact, sizeof(compact), &compact_len) == PM3_SUCCESS &&
|
||||
compact_len > 0) {
|
||||
decoded_len = hex_to_bytes(compact, decoded, sizeof(decoded));
|
||||
}
|
||||
@@ -740,7 +667,7 @@ static int pcrypto_parse_ec_public_base64(const char *input,
|
||||
|
||||
char compact[PCRYPTO_MAX_KEY_INPUT] = {0};
|
||||
size_t compact_len = 0;
|
||||
int res = pcrypto_copy_without_whitespace(input, compact, sizeof(compact), &compact_len);
|
||||
int res = str_copy_without_whitespace(input, compact, sizeof(compact), &compact_len);
|
||||
if (res != PM3_SUCCESS || compact_len == 0) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
@@ -777,7 +704,7 @@ static int pcrypto_parse_ec_public_text(const char *input, bool allow_file_path,
|
||||
return PM3_EOVFLOW;
|
||||
}
|
||||
memcpy(normalized, input, input_len + 1);
|
||||
pcrypto_trim_ascii_inplace(normalized);
|
||||
str_trim_ascii_inplace(normalized);
|
||||
|
||||
if (normalized[0] == '\0') {
|
||||
return PM3_EINVARG;
|
||||
@@ -793,14 +720,14 @@ static int pcrypto_parse_ec_public_text(const char *input, bool allow_file_path,
|
||||
}
|
||||
|
||||
// Only unescape after path resolution fails
|
||||
pcrypto_unescape_newlines_inplace(normalized);
|
||||
str_unescape_newlines_inplace(normalized);
|
||||
|
||||
// Try as hex string
|
||||
uint8_t decoded[PCRYPTO_MAX_KEY_INPUT] = {0};
|
||||
int decoded_len = -1;
|
||||
char compact[PCRYPTO_MAX_KEY_INPUT] = {0};
|
||||
size_t compact_len = 0;
|
||||
if (pcrypto_copy_without_whitespace(normalized, compact, sizeof(compact), &compact_len) == PM3_SUCCESS &&
|
||||
if (str_copy_without_whitespace(normalized, compact, sizeof(compact), &compact_len) == PM3_SUCCESS &&
|
||||
compact_len > 0) {
|
||||
decoded_len = hex_to_bytes(compact, decoded, sizeof(decoded));
|
||||
}
|
||||
|
||||
+161
-6
@@ -127,20 +127,176 @@ int fileExists(const char *filename) {
|
||||
* @param filename
|
||||
* @return
|
||||
*/
|
||||
static bool is_directory(const char *filename) {
|
||||
bool path_is_directory(const char *path) {
|
||||
if (path == NULL) {
|
||||
return false;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
struct _stat st;
|
||||
if (_stat(filename, &st) == -1)
|
||||
if (_stat(path, &st) == -1)
|
||||
return false;
|
||||
#else
|
||||
struct stat st;
|
||||
// stat(filename, &st);
|
||||
if (lstat(filename, &st) == -1)
|
||||
if (lstat(path, &st) == -1)
|
||||
return false;
|
||||
#endif
|
||||
return S_ISDIR(st.st_mode) != 0;
|
||||
}
|
||||
|
||||
bool path_is_regular_file(const char *path) {
|
||||
if (path == NULL) {
|
||||
return false;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
struct _stat st;
|
||||
if (_stat(path, &st) == -1)
|
||||
return false;
|
||||
#else
|
||||
struct stat st;
|
||||
if (stat(path, &st) == -1)
|
||||
return false;
|
||||
#endif
|
||||
return S_ISREG(st.st_mode) != 0;
|
||||
}
|
||||
|
||||
const char *path_basename(const char *path) {
|
||||
if (path == NULL) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const char *base = strrchr(path, '/');
|
||||
const char *base_win = strrchr(path, '\\');
|
||||
if (base == NULL || (base_win != NULL && base_win > base)) {
|
||||
base = base_win;
|
||||
}
|
||||
return (base == NULL) ? path : (base + 1);
|
||||
}
|
||||
|
||||
void path_basename_without_ext(const char *path, char *out, size_t out_len) {
|
||||
if (out == NULL || out_len == 0) {
|
||||
return;
|
||||
}
|
||||
out[0] = '\0';
|
||||
|
||||
const char *base = path_basename(path);
|
||||
if (base[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(out, out_len, "%s", base);
|
||||
char *dot = strrchr(out, '.');
|
||||
if (dot != NULL && dot != out) {
|
||||
*dot = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
static int qsort_path_cmp(const void *a, const void *b) {
|
||||
const char *pa = (const char *)a;
|
||||
const char *pb = (const char *)b;
|
||||
return strcmp(pa, pb);
|
||||
}
|
||||
|
||||
static char *path_list_slot(char *paths, size_t path_len, size_t index) {
|
||||
return paths + (index * path_len);
|
||||
}
|
||||
|
||||
int collect_file_paths_recursive(const char *dirpath, char *paths, size_t path_len,
|
||||
size_t max_paths, size_t *count, bool include_hidden, size_t max_depth) {
|
||||
if (dirpath == NULL || paths == NULL || path_len == 0 || count == NULL) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
DIR *dir = opendir(dirpath);
|
||||
if (dir == NULL) {
|
||||
return PM3_EFILE;
|
||||
}
|
||||
|
||||
struct dirent *entry = NULL;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0 ||
|
||||
(include_hidden == false && entry->d_name[0] == '.')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char *fullpath = calloc(path_len, sizeof(char));
|
||||
if (fullpath == NULL) {
|
||||
closedir(dir);
|
||||
return PM3_EMALLOC;
|
||||
}
|
||||
const char *sep = "";
|
||||
size_t dir_len = strlen(dirpath);
|
||||
if (dir_len > 0 && dirpath[dir_len - 1] != '/' && dirpath[dir_len - 1] != '\\') {
|
||||
sep = PATHSEP;
|
||||
}
|
||||
if (snprintf(fullpath, path_len, "%s%s%s", dirpath, sep, entry->d_name) >= (int)path_len) {
|
||||
free(fullpath);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (path_is_directory(fullpath)) {
|
||||
if (max_depth == 0) {
|
||||
free(fullpath);
|
||||
continue;
|
||||
}
|
||||
int res = collect_file_paths_recursive(fullpath, paths, path_len, max_paths, count, include_hidden, max_depth - 1);
|
||||
free(fullpath);
|
||||
if (res != PM3_SUCCESS) {
|
||||
closedir(dir);
|
||||
return res;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!path_is_regular_file(fullpath)) {
|
||||
free(fullpath);
|
||||
continue;
|
||||
}
|
||||
if (*count >= max_paths) {
|
||||
free(fullpath);
|
||||
closedir(dir);
|
||||
return PM3_EOVFLOW;
|
||||
}
|
||||
if (snprintf(path_list_slot(paths, path_len, *count), path_len, "%s", fullpath) >= (int)path_len) {
|
||||
free(fullpath);
|
||||
continue;
|
||||
}
|
||||
free(fullpath);
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
int collect_resource_file_paths(const char *resource_dir, char *paths, size_t path_len,
|
||||
size_t max_paths, size_t *count, bool include_hidden, size_t max_depth) {
|
||||
if (resource_dir == NULL || resource_dir[0] == '\0' || paths == NULL || path_len == 0 || count == NULL) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
char *rootdir = NULL;
|
||||
int res = searchFile(&rootdir, RESOURCES_SUBDIR, resource_dir, "", true);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (!path_is_directory(rootdir)) {
|
||||
free(rootdir);
|
||||
return PM3_EFILE;
|
||||
}
|
||||
|
||||
*count = 0;
|
||||
res = collect_file_paths_recursive(rootdir, paths, path_len, max_paths, count, include_hidden, max_depth);
|
||||
free(rootdir);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
|
||||
qsort(paths, *count, path_len, qsort_path_cmp);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
bool setDefaultPath(savePaths_t pathIndex, const char *path) {
|
||||
|
||||
if (pathIndex < spItemCount) {
|
||||
@@ -2884,7 +3040,7 @@ static int filelist(const char *path, const char *ext, uint8_t last, bool tentat
|
||||
tmp_fullpath[1023] = 0x00;
|
||||
strncat(tmp_fullpath, namelist[i]->d_name, strlen(tmp_fullpath) - 1);
|
||||
|
||||
if (is_directory(tmp_fullpath)) {
|
||||
if (path_is_directory(tmp_fullpath)) {
|
||||
|
||||
char newpath[1024];
|
||||
if (strcmp(namelist[i]->d_name, ".") == 0 || strcmp(namelist[i]->d_name, "..") == 0)
|
||||
@@ -3144,7 +3300,7 @@ int searchFile(char **foundpath, const char *pm3dir, const char *searchname, con
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
if (is_directory(searchname)) {
|
||||
if (path_is_directory(searchname)) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
@@ -3397,4 +3553,3 @@ int pm3_save_fm11rf08s_nonces(const char *fn, iso14a_fm11rf08s_nonces_with_data_
|
||||
}
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -114,6 +114,42 @@ typedef enum {
|
||||
|
||||
int fileExists(const char *filename);
|
||||
|
||||
/**
|
||||
* @brief Check whether a path exists and is a directory.
|
||||
*/
|
||||
bool path_is_directory(const char *path);
|
||||
|
||||
/**
|
||||
* @brief Check whether a path exists and is a regular file.
|
||||
*/
|
||||
bool path_is_regular_file(const char *path);
|
||||
|
||||
/**
|
||||
* @brief Return the final path component, or an empty string for NULL input.
|
||||
*/
|
||||
const char *path_basename(const char *path);
|
||||
|
||||
/**
|
||||
* @brief Copy the final path component without its last file extension.
|
||||
*/
|
||||
void path_basename_without_ext(const char *path, char *out, size_t out_len);
|
||||
|
||||
/**
|
||||
* @brief Recursively collect regular file paths under a directory into fixed-size slots.
|
||||
*
|
||||
* A max_depth of 0 scans only dirpath and does not descend into subdirectories.
|
||||
*/
|
||||
int collect_file_paths_recursive(const char *dirpath, char *paths, size_t path_len,
|
||||
size_t max_paths, size_t *count, bool include_hidden, size_t max_depth);
|
||||
|
||||
/**
|
||||
* @brief Resolve a resources subdirectory and recursively collect regular file paths from it.
|
||||
*
|
||||
* A max_depth of 0 scans only the resolved resource_dir and does not descend into subdirectories.
|
||||
*/
|
||||
int collect_resource_file_paths(const char *resource_dir, char *paths, size_t path_len,
|
||||
size_t max_paths, size_t *count, bool include_hidden, size_t max_depth);
|
||||
|
||||
// set a path in the path list g_session.defaultPaths
|
||||
bool setDefaultPath(savePaths_t pathIndex, const char *path);
|
||||
|
||||
|
||||
@@ -1944,3 +1944,75 @@ size_t unduplicate(uint8_t *d, size_t n, const uint8_t item_n) {
|
||||
|
||||
return write_index;
|
||||
}
|
||||
|
||||
void str_trim_ascii_inplace(char *s) {
|
||||
if (s == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t start = 0;
|
||||
size_t len = strlen(s);
|
||||
while (start < len && isspace((unsigned char)s[start])) {
|
||||
start++;
|
||||
}
|
||||
while (len > start && isspace((unsigned char)s[len - 1])) {
|
||||
len--;
|
||||
}
|
||||
|
||||
if (start > 0) {
|
||||
memmove(s, s + start, len - start);
|
||||
}
|
||||
s[len - start] = '\0';
|
||||
}
|
||||
|
||||
void str_unescape_newlines_inplace(char *s) {
|
||||
if (s == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t read_pos = 0;
|
||||
size_t write_pos = 0;
|
||||
size_t len = strlen(s);
|
||||
while (read_pos < len) {
|
||||
if (s[read_pos] == '\\' && (read_pos + 1) < len) {
|
||||
char esc = s[read_pos + 1];
|
||||
if (esc == 'n') {
|
||||
s[write_pos++] = '\n';
|
||||
read_pos += 2;
|
||||
continue;
|
||||
}
|
||||
if (esc == 'r') {
|
||||
s[write_pos++] = '\r';
|
||||
read_pos += 2;
|
||||
continue;
|
||||
}
|
||||
if (esc == 't') {
|
||||
s[write_pos++] = '\t';
|
||||
read_pos += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
s[write_pos++] = s[read_pos++];
|
||||
}
|
||||
s[write_pos] = '\0';
|
||||
}
|
||||
|
||||
int str_copy_without_whitespace(const char *src, char *dst, size_t dst_size, size_t *dst_len) {
|
||||
if (src == NULL || dst == NULL || dst_len == NULL || dst_size == 0) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
size_t out = 0;
|
||||
for (size_t i = 0; src[i] != '\0'; i++) {
|
||||
if (isspace((unsigned char)src[i])) {
|
||||
continue;
|
||||
}
|
||||
if ((out + 1) >= dst_size) {
|
||||
return PM3_EOVFLOW;
|
||||
}
|
||||
dst[out++] = src[i];
|
||||
}
|
||||
dst[out] = '\0';
|
||||
*dst_len = out;
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -215,4 +215,19 @@ uint8_t get_highest_frequency(const uint8_t *d, uint8_t n);
|
||||
|
||||
size_t unduplicate(uint8_t *d, size_t n, const uint8_t item_n);
|
||||
|
||||
/**
|
||||
* @brief Trim leading and trailing ASCII whitespace from a mutable string.
|
||||
*/
|
||||
void str_trim_ascii_inplace(char *s);
|
||||
|
||||
/**
|
||||
* @brief Replace escaped \n, \r, and \t sequences with their control characters in-place.
|
||||
*/
|
||||
void str_unescape_newlines_inplace(char *s);
|
||||
|
||||
/**
|
||||
* @brief Copy a string while dropping all ASCII whitespace characters.
|
||||
*/
|
||||
int str_copy_without_whitespace(const char *src, char *dst, size_t dst_size, size_t *dst_len);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user