7#include <ascii-chat/options/validation.h>
14#include <ascii-chat/common.h>
15#include <ascii-chat/log/logging.h>
16#include <ascii-chat/platform/terminal.h>
17#include <ascii-chat/platform/util.h>
18#include <ascii-chat/util/ip.h>
19#include <ascii-chat/util/parsing.h>
20#include <ascii-chat/video/palette.h>
21#include <ascii-chat/options/options.h>
41static int validate_int_range(
const char *value_str,
int min,
int max,
const char *param_name,
char *error_msg,
42 size_t error_msg_size) {
43 if (!value_str || strlen(value_str) == 0) {
45 SAFE_SNPRINTF(error_msg, error_msg_size,
"%s value is required", param_name);
51 if (val == INT_MIN || val < min || val > max) {
54 SAFE_SNPRINTF(error_msg, error_msg_size,
"Invalid %s '%s'. Must be exactly %d.", param_name, value_str, min);
55 }
else if (min == 1 && max == INT_MAX) {
56 SAFE_SNPRINTF(error_msg, error_msg_size,
"Invalid %s '%s'. Must be a positive integer.", param_name, value_str);
57 }
else if (min == 0 && max == INT_MAX) {
58 SAFE_SNPRINTF(error_msg, error_msg_size,
"Invalid %s '%s'. Must be a non-negative integer.", param_name,
61 SAFE_SNPRINTF(error_msg, error_msg_size,
"Invalid %s '%s'. Must be between %d and %d.", param_name, value_str,
75 if (!value_str || strlen(value_str) == 0) {
77 SAFE_SNPRINTF(error_msg, error_msg_size,
"Port value is required");
84 if (
parse_port(value_str, &port_num) != ASCIICHAT_OK) {
86 SAFE_SNPRINTF(error_msg, error_msg_size,
"Invalid port value '%s'. Port must be a number between 1 and 65535.",
99 const options_t *opts = (
const options_t *)options_struct;
107 if (opts->port < 1 || opts->port > 65535) {
122 int result = validate_int_range(value_str, 1, INT_MAX,
"Value", error_msg, error_msg_size);
123 return (result == INT_MIN) ? -1 : result;
131 int result = validate_int_range(value_str, 0, INT_MAX,
"Value", error_msg, error_msg_size);
132 return (result == INT_MIN) ? -1 : result;
142 SAFE_SNPRINTF(error_msg, error_msg_size,
"Color mode value is required");
147 if (strcmp(value_str,
"auto") == 0) {
148 return COLOR_MODE_AUTO;
150 if (strcmp(value_str,
"none") == 0 || strcmp(value_str,
"mono") == 0) {
151 return COLOR_MODE_NONE;
153 if (strcmp(value_str,
"16") == 0 || strcmp(value_str,
"16color") == 0) {
154 return COLOR_MODE_16_COLOR;
156 if (strcmp(value_str,
"256") == 0 || strcmp(value_str,
"256color") == 0) {
157 return COLOR_MODE_256_COLOR;
159 if (strcmp(value_str,
"truecolor") == 0 || strcmp(value_str,
"24bit") == 0) {
160 return COLOR_MODE_TRUECOLOR;
163 SAFE_SNPRINTF(error_msg, error_msg_size,
164 "Invalid color mode '%s'. Valid modes: auto, none, mono, 16, 256, truecolor", value_str);
176 SAFE_SNPRINTF(error_msg, error_msg_size,
"Render mode value is required");
181 if (strcmp(value_str,
"foreground") == 0 || strcmp(value_str,
"fg") == 0) {
182 return RENDER_MODE_FOREGROUND;
184 if (strcmp(value_str,
"background") == 0 || strcmp(value_str,
"bg") == 0) {
185 return RENDER_MODE_BACKGROUND;
187 if (strcmp(value_str,
"half-block") == 0 || strcmp(value_str,
"halfblock") == 0) {
188 return RENDER_MODE_HALF_BLOCK;
191 SAFE_SNPRINTF(error_msg, error_msg_size,
192 "Invalid render mode '%s'. Valid modes: foreground, background, half-block", value_str);
204 SAFE_SNPRINTF(error_msg, error_msg_size,
"Palette value is required");
209 if (strcmp(value_str,
"standard") == 0) {
210 return PALETTE_STANDARD;
211 }
else if (strcmp(value_str,
"blocks") == 0) {
212 return PALETTE_BLOCKS;
213 }
else if (strcmp(value_str,
"digital") == 0) {
214 return PALETTE_DIGITAL;
215 }
else if (strcmp(value_str,
"minimal") == 0) {
216 return PALETTE_MINIMAL;
217 }
else if (strcmp(value_str,
"cool") == 0) {
219 }
else if (strcmp(value_str,
"custom") == 0) {
220 return PALETTE_CUSTOM;
223 SAFE_SNPRINTF(error_msg, error_msg_size,
224 "Invalid palette '%s'. Valid palettes: standard, blocks, digital, minimal, cool, custom",
238 SAFE_SNPRINTF(error_msg, error_msg_size,
"Log level value is required");
257 SAFE_SNPRINTF(error_msg, error_msg_size,
258 "Invalid log level '%s'. Valid levels: dev, debug, info, warn, error, fatal", value_str);
270 char *error_msg,
size_t error_msg_size) {
272 if (!value_str || strlen(value_str) == 0) {
274 SAFE_SNPRINTF(error_msg, error_msg_size,
"Address value is required");
280 char parsed_addr[OPTIONS_BUFF_SIZE];
282 value_str = parsed_addr;
287 SAFE_SNPRINTF(parsed_address, address_size,
"%s", value_str);
292 SAFE_SNPRINTF(parsed_address, address_size,
"%s", value_str);
296 if (strchr(value_str,
'.') != NULL) {
298 SAFE_SNPRINTF(error_msg, error_msg_size,
299 "Invalid IP address format '%s'. IPv4 addresses must have exactly 4 octets.", value_str);
305 char resolved_ip[OPTIONS_BUFF_SIZE];
306 if (platform_resolve_hostname_to_ipv4(value_str, resolved_ip,
sizeof(resolved_ip)) == 0) {
307 SAFE_SNPRINTF(parsed_address, address_size,
"%s", resolved_ip);
311 SAFE_SNPRINTF(error_msg, error_msg_size,
"Failed to resolve hostname '%s' to IP address.", value_str);
322 if (!value_str || strlen(value_str) == 0) {
324 SAFE_SNPRINTF(error_msg, error_msg_size,
"Value is required");
330 float val = strtof(value_str, &endptr);
331 if (*endptr !=
'\0' || value_str == endptr) {
333 SAFE_SNPRINTF(error_msg, error_msg_size,
"Invalid float value '%s'. Must be a number.", value_str);
339 SAFE_SNPRINTF(error_msg, error_msg_size,
"Value must be non-negative (got %.2f)", val);
351 if (!value_str || strlen(value_str) == 0) {
353 SAFE_SNPRINTF(error_msg, error_msg_size,
"Volume value is required");
359 float val = strtof(value_str, &endptr);
360 if (*endptr !=
'\0' || value_str == endptr) {
362 SAFE_SNPRINTF(error_msg, error_msg_size,
"Invalid volume value '%s'. Must be a number.", value_str);
366 if (val < 0.0f || val > 1.0f) {
368 SAFE_SNPRINTF(error_msg, error_msg_size,
"Volume must be between 0.0 and 1.0 (got %.2f)", val);
380 int result = validate_int_range(value_str, 1, 32,
"Max clients", error_msg, error_msg_size);
381 return (result == INT_MIN) ? -1 : result;
389 int result = validate_int_range(value_str, 1, 9,
"Compression level", error_msg, error_msg_size);
390 return (result == INT_MIN) ? -1 : result;
398 int result = validate_int_range(value_str, 1, 144,
"FPS", error_msg, error_msg_size);
399 return (result == INT_MIN) ? -1 : result;
411 if (!value_str || strlen(value_str) == 0) {
413 SAFE_SNPRINTF(error_msg, error_msg_size,
"Reconnect value is required");
428 if (val == INT_MIN) {
430 SAFE_SNPRINTF(error_msg, error_msg_size,
"Invalid reconnect value '%s'. Use 'off', 'auto', or a number 0-999.",
443 if (val < 1 || val > 999) {
445 SAFE_SNPRINTF(error_msg, error_msg_size,
"Invalid reconnect count '%s'. Must be 'off', 'auto', or 1-999.",
458 if (!value_str || strlen(value_str) == 0) {
460 SAFE_SNPRINTF(error_msg, error_msg_size,
"Device index value is required");
466 if (index == INT_MIN) {
468 SAFE_SNPRINTF(error_msg, error_msg_size,
469 "Invalid device index '%s'. Must be -1 (default) or a non-negative integer.", value_str);
477 SAFE_SNPRINTF(error_msg, error_msg_size,
478 "Invalid device index '%d'. Must be -1 (default) or a non-negative integer.", index);
492 SAFE_SNPRINTF(error_msg, error_msg_size,
"Password value is required");
497 size_t len = strlen(value_str);
500 SAFE_SNPRINTF(error_msg, error_msg_size,
"Password too short (%zu chars). Must be at least 8 characters.", len);
506 SAFE_SNPRINTF(error_msg, error_msg_size,
"Password too long (%zu chars). Must be at most 256 characters.", len);
529 if (!opts || !argv) {
530 log_error(
"options_collect_identity_keys: Invalid arguments");
534 size_t key_count = 0;
537 for (
int i = 1; i < argc && key_count < MAX_IDENTITY_KEYS; i++) {
538 const char *arg = argv[i];
541 bool is_key_flag =
false;
542 const char *key_value = NULL;
544 if (strcmp(arg,
"--key") == 0 || strcmp(arg,
"-K") == 0) {
547 key_value = argv[i + 1];
551 }
else if (strncmp(arg,
"--key=", 6) == 0) {
557 if (is_key_flag && key_value && strlen(key_value) > 0) {
561 if (stat(key_value, &st) != 0) {
562 log_error(
"Key file not found: %s", key_value);
563 SET_ERRNO(ERROR_CRYPTO_KEY,
"Key file not found: %s", key_value);
567 if ((st.st_mode & S_IFMT) != S_IFREG) {
568 log_error(
"Key path is not a regular file: %s", key_value);
569 SET_ERRNO(ERROR_CRYPTO_KEY,
"Key path is not a regular file: %s", key_value);
575 SAFE_STRNCPY(opts->identity_keys[key_count], key_value, OPTIONS_BUFF_SIZE);
578 if (key_count == 0) {
579 SAFE_STRNCPY(opts->encrypt_key, key_value, OPTIONS_BUFF_SIZE);
583 log_debug(
"Collected identity key #%zu: %s", key_count, key_value);
587 opts->num_identity_keys = key_count;
590 log_info(
"Collected %zu identity key(s) for multi-key support", key_count);
593 return (
int)key_count;
597 if (!key_path || key_path[0] ==
'\0') {
600 return (strncmp(key_path,
"github:", 7) == 0 ||
601 strncmp(key_path,
"gitlab:", 7) == 0 ||
602 strncmp(key_path,
"gpg:", 4) == 0 ||
603 strncmp(key_path,
"http://", 7) == 0 ||
604 strncmp(key_path,
"https://", 8) == 0);
int is_valid_ipv4(const char *ip)
int is_valid_ipv6(const char *ip)
int parse_ipv6_address(const char *input, char *output, size_t output_size)
int strtoint_safe(const char *str)
asciichat_error_t parse_port(const char *str, uint16_t *out_port)
float validate_opt_volume(const char *value_str, char *error_msg, size_t error_msg_size)
int validate_opt_positive_int(const char *value_str, char *error_msg, size_t error_msg_size)
int validate_opt_render_mode(const char *value_str, char *error_msg, size_t error_msg_size)
int validate_opt_color_mode(const char *value_str, char *error_msg, size_t error_msg_size)
int validate_opt_palette(const char *value_str, char *error_msg, size_t error_msg_size)
int validate_opt_non_negative_int(const char *value_str, char *error_msg, size_t error_msg_size)
int validate_opt_password(const char *value_str, char *error_msg, size_t error_msg_size)
int validate_opt_fps(const char *value_str, char *error_msg, size_t error_msg_size)
float validate_opt_float_non_negative(const char *value_str, char *error_msg, size_t error_msg_size)
int validate_opt_device_index(const char *value_str, char *error_msg, size_t error_msg_size)
int validate_opt_reconnect(const char *value_str, char *error_msg, size_t error_msg_size)
bool validate_port_callback(const void *options_struct, char **error_msg)
int validate_opt_max_clients(const char *value_str, char *error_msg, size_t error_msg_size)
int validate_opt_ip_address(const char *value_str, char *parsed_address, size_t address_size, bool is_client, char *error_msg, size_t error_msg_size)
bool is_remote_key_path(const char *key_path)
int validate_opt_port(const char *value_str, char *error_msg, size_t error_msg_size)
int options_collect_identity_keys(options_t *opts, int argc, char *argv[])
int validate_opt_compression_level(const char *value_str, char *error_msg, size_t error_msg_size)
int validate_opt_log_level(const char *value_str, char *error_msg, size_t error_msg_size)