config: fix tilde expansion for wallpaper paths

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I45b7a520f1959886793ded62f1ed2cd96a6a6964
This commit is contained in:
raf 2025-10-02 01:13:16 +03:00
commit 74fed80a26
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
3 changed files with 202 additions and 28 deletions

View file

@ -67,20 +67,32 @@ static int add_output_mapping(chroma_config_t *config, const char *output_name,
return CHROMA_ERROR_MEMORY; return CHROMA_ERROR_MEMORY;
} }
// Validate string lengths to prevent buffer overflow
size_t output_len = strlen(output_name);
size_t path_len = strlen(image_path);
if (output_len >= sizeof(config->mappings[0].output_name)) {
chroma_log("ERROR", "Output name too long: %s (max %zu)", output_name,
sizeof(config->mappings[0].output_name) - 1);
return CHROMA_ERROR_CONFIG;
}
if (path_len >= sizeof(config->mappings[0].image_path)) {
chroma_log("ERROR", "Image path too long: %s (max %zu)", image_path,
sizeof(config->mappings[0].image_path) - 1);
return CHROMA_ERROR_CONFIG;
}
chroma_config_mapping_t *mapping = &config->mappings[config->mapping_count]; chroma_config_mapping_t *mapping = &config->mappings[config->mapping_count];
strncpy(mapping->output_name, output_name, sizeof(mapping->output_name) - 1); strcpy(mapping->output_name, output_name);
mapping->output_name[sizeof(mapping->output_name) - 1] = '\0'; strcpy(mapping->image_path, image_path);
strncpy(mapping->image_path, image_path, sizeof(mapping->image_path) - 1);
mapping->image_path[sizeof(mapping->image_path) - 1] = '\0';
config->mapping_count++; config->mapping_count++;
chroma_log("DEBUG", "Added mapping: %s -> %s", output_name, image_path); chroma_log("DEBUG", "Added mapping: %s -> %s", output_name, image_path);
chroma_log("TRACE", "Output mapping %d: '%s' -> '%s' (path length: %zu)", chroma_log("TRACE", "Output mapping %d: '%s' -> '%s' (path length: %zu)",
config->mapping_count, output_name, image_path, config->mapping_count, output_name, image_path, path_len);
strlen(image_path));
return CHROMA_OK; return CHROMA_OK;
} }
@ -138,11 +150,29 @@ static int parse_config_line(chroma_config_t *config, char *line,
// Parse configuration options // Parse configuration options
if (strcasecmp(key, "default_image") == 0) { if (strcasecmp(key, "default_image") == 0) {
strncpy(config->default_image, value, sizeof(config->default_image) - 1); char *expanded_path = chroma_expand_path(value);
config->default_image[sizeof(config->default_image) - 1] = '\0'; const char *path_to_use = expanded_path ? expanded_path : value;
chroma_log("DEBUG", "Set default image: %s", value); size_t path_len = strlen(path_to_use);
chroma_log("TRACE", "Default image path set: length=%zu, expanded='%s'",
strlen(value), value); if (path_len >= sizeof(config->default_image)) {
chroma_log("ERROR", "Default image path too long: %s (max %zu)",
path_to_use, sizeof(config->default_image) - 1);
if (expanded_path) {
free(expanded_path);
}
return CHROMA_ERROR_CONFIG;
}
strcpy(config->default_image, path_to_use);
if (expanded_path) {
chroma_log("DEBUG", "Set default image: %s -> %s", value, expanded_path);
chroma_log("TRACE", "Default image path set: length=%zu, expanded='%s'",
path_len, expanded_path);
free(expanded_path);
} else {
chroma_log("WARN", "Failed to expand path, using original: %s", value);
}
} else if (strcasecmp(key, "daemon") == 0 || } else if (strcasecmp(key, "daemon") == 0 ||
strcasecmp(key, "daemon_mode") == 0) { strcasecmp(key, "daemon_mode") == 0) {
config->daemon_mode = parse_bool(value); config->daemon_mode = parse_bool(value);
@ -160,18 +190,33 @@ static int parse_config_line(chroma_config_t *config, char *line,
return CHROMA_OK; return CHROMA_OK;
} }
// Expand path before validation and storage
char *expanded_path = chroma_expand_path(value);
const char *path_to_validate = expanded_path ? expanded_path : value;
// Validate image path // Validate image path
if (chroma_image_validate(value) != CHROMA_OK) { if (chroma_image_validate(path_to_validate) != CHROMA_OK) {
chroma_log("WARN", "Invalid image path for output %s: %s", output_name, chroma_log("WARN", "Invalid image path for output %s: %s (expanded: %s)",
value); output_name, value, path_to_validate);
if (expanded_path) {
free(expanded_path);
}
return CHROMA_OK; return CHROMA_OK;
} }
if (add_output_mapping(config, output_name, value) != CHROMA_OK) { if (add_output_mapping(config, output_name, path_to_validate) !=
CHROMA_OK) {
chroma_log("ERROR", "Failed to add output mapping: %s -> %s", output_name, chroma_log("ERROR", "Failed to add output mapping: %s -> %s", output_name,
value); path_to_validate);
if (expanded_path) {
free(expanded_path);
}
return CHROMA_ERROR_CONFIG; return CHROMA_ERROR_CONFIG;
} }
if (expanded_path) {
free(expanded_path);
}
} else { } else {
chroma_log("WARN", "Unknown configuration key line %d: %s", line_number, chroma_log("WARN", "Unknown configuration key line %d: %s", line_number,
key); key);

View file

@ -4,6 +4,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <strings.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "../include/chroma.h" #include "../include/chroma.h"

View file

@ -94,18 +94,138 @@ void chroma_cleanup_signals(void) {
g_config_file = NULL; g_config_file = NULL;
} }
// Expand tilde in path // Expand environment variables in a string
static char *expand_env_vars(const char *str) {
if (!str || strchr(str, '$') == NULL) {
return strdup(str);
}
char *result = strdup("");
if (!result) {
return NULL;
}
const char *p = str;
while (*p) {
if (*p == '$') {
p++;
if (*p == '{') {
// ${VAR} format
p++;
const char *end = strchr(p, '}');
if (!end) {
// No closing brace, treat as literal
char *tmp = realloc(result, strlen(result) + 2);
if (!tmp) {
free(result);
return NULL;
}
result = tmp;
strcat(result, "${");
break;
}
size_t var_len = end - p;
char *var_name = malloc(var_len + 1);
if (!var_name) {
free(result);
return NULL;
}
strncpy(var_name, p, var_len);
var_name[var_len] = '\0';
const char *var_value = getenv(var_name);
if (var_value) {
char *tmp = realloc(result, strlen(result) + strlen(var_value) + 1);
if (!tmp) {
free(var_name);
free(result);
return NULL;
}
result = tmp;
strcat(result, var_value);
}
free(var_name);
p = end + 1;
} else {
// $VAR format
const char *start = p;
while (*p && (isalnum((unsigned char)*p) || *p == '_')) {
p++;
}
if (p == start) {
// Not a valid variable name, treat $ as literal
char *tmp = realloc(result, strlen(result) + 2);
if (!tmp) {
free(result);
return NULL;
}
result = tmp;
strcat(result, "$");
} else {
size_t var_len = p - start;
char *var_name = malloc(var_len + 1);
if (!var_name) {
free(result);
return NULL;
}
strncpy(var_name, start, var_len);
var_name[var_len] = '\0';
const char *var_value = getenv(var_name);
if (var_value) {
char *tmp = realloc(result, strlen(result) + strlen(var_value) + 1);
if (!tmp) {
free(var_name);
free(result);
return NULL;
}
result = tmp;
strcat(result, var_value);
}
free(var_name);
}
}
} else {
// Regular character
size_t len = strlen(result);
char *tmp = realloc(result, len + 2);
if (!tmp) {
free(result);
return NULL;
}
result = tmp;
result[len] = *p;
result[len + 1] = '\0';
p++;
}
}
return result;
}
// Expand tilde and environment variables in path
char *chroma_expand_path(const char *path) { char *chroma_expand_path(const char *path) {
if (!path) { if (!path) {
return NULL; return NULL;
} }
if (path[0] != '~') { // First expand environment variables
return strdup(path); char *env_expanded = expand_env_vars(path);
if (!env_expanded) {
return NULL;
}
// Then expand tilde if present
if (env_expanded[0] != '~') {
return env_expanded;
} }
const char *home; const char *home;
if (path[1] == '/' || path[1] == '\0') { if (env_expanded[1] == '/' || env_expanded[1] == '\0') {
// ~/... or just ~ // ~/... or just ~
home = getenv("HOME"); home = getenv("HOME");
if (!home) { if (!home) {
@ -116,34 +236,39 @@ char *chroma_expand_path(const char *path) {
} }
if (!home) { if (!home) {
chroma_log("ERROR", "Could not determine home directory"); chroma_log("ERROR", "Could not determine home directory");
free(env_expanded);
return strdup(path); // Return original path as fallback return strdup(path); // Return original path as fallback
} }
size_t home_len = strlen(home); size_t home_len = strlen(home);
size_t path_len = strlen(path); size_t path_len = strlen(env_expanded);
char *expanded = malloc(home_len + path_len); // -1 for ~ +1 for \0 char *expanded = malloc(home_len + path_len); // -1 for ~ +1 for \0
if (!expanded) { if (!expanded) {
chroma_log("ERROR", "Failed to allocate memory for path expansion"); chroma_log("ERROR", "Failed to allocate memory for path expansion");
free(env_expanded);
return strdup(path); return strdup(path);
} }
strcpy(expanded, home); strcpy(expanded, home);
if (path[1] == '/') { if (env_expanded[1] == '/') {
strcat(expanded, path + 1); strcat(expanded, env_expanded + 1);
} }
free(env_expanded);
return expanded; return expanded;
} else { } else {
// ~user/... // ~user/...
const char *slash = strchr(path, '/'); const char *slash = strchr(env_expanded, '/');
size_t user_len = slash ? (size_t)(slash - path - 1) : strlen(path) - 1; size_t user_len =
slash ? (size_t)(slash - env_expanded - 1) : strlen(env_expanded) - 1;
char *username = malloc(user_len + 1); char *username = malloc(user_len + 1);
if (!username) { if (!username) {
free(env_expanded);
return strdup(path); return strdup(path);
} }
strncpy(username, path + 1, user_len); strncpy(username, env_expanded + 1, user_len);
username[user_len] = '\0'; username[user_len] = '\0';
struct passwd *pw = getpwnam(username); struct passwd *pw = getpwnam(username);
@ -151,6 +276,7 @@ char *chroma_expand_path(const char *path) {
if (!pw) { if (!pw) {
chroma_log("ERROR", "User not found: %s", username); chroma_log("ERROR", "User not found: %s", username);
free(username); free(username);
free(env_expanded);
return strdup(path); return strdup(path);
} }
@ -160,6 +286,7 @@ char *chroma_expand_path(const char *path) {
size_t remaining_len = slash ? strlen(slash) : 0; size_t remaining_len = slash ? strlen(slash) : 0;
char *expanded = malloc(home_len + remaining_len + 1); char *expanded = malloc(home_len + remaining_len + 1);
if (!expanded) { if (!expanded) {
free(env_expanded);
return strdup(path); return strdup(path);
} }
@ -168,6 +295,7 @@ char *chroma_expand_path(const char *path) {
strcat(expanded, slash); strcat(expanded, slash);
} }
free(env_expanded);
return expanded; return expanded;
} }
} }
@ -413,4 +541,4 @@ void chroma_format_memory_size(size_t bytes, char *buffer, size_t buffer_size) {
} }
// Cleanup utility functions // Cleanup utility functions
void chroma_utils_cleanup(void) { chroma_cleanup_signals(); } void chroma_utils_cleanup(void) { chroma_cleanup_signals(); }