The string contains file's path, newline and file contents.
If the newline is missing, the file is read immediately.
#include "array.h"
#include "str.h"
#include "str-parse.h"
+#include "read-full.h"
#include "settings-parser.h"
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
struct boollist_removal {
ARRAY_TYPE(const_string) *array;
const char *key_suffix;
return 0;
}
+static int
+get_file(struct setting_parser_context *ctx, bool dup_value, const char **value)
+{
+ if (**value == '\0')
+ return 0;
+ const char *content = strchr(*value, '\n');
+ if (content != NULL) {
+ if (dup_value)
+ *value = p_strdup(ctx->set_pool, *value);
+ return 0;
+ }
+
+ const char *error;
+ if (settings_parse_read_file(*value, *value, ctx->set_pool,
+ value, &error) < 0) {
+ settings_parser_set_error(ctx, error);
+ return -1;
+ }
+ return 0;
+}
+
static int
get_in_port_zero(struct setting_parser_context *ctx, const char *value,
in_port_t *result_r)
return 0;
}
+int settings_parse_read_file(const char *path, const char *value_path,
+ pool_t pool,
+ const char **output_r, const char **error_r)
+{
+ struct stat st;
+ int fd;
+
+ if ((fd = open(path, O_RDONLY)) == -1) {
+ *error_r = t_strdup_printf("open(%s) failed: %m", path);
+ return -1;
+ }
+ if (fstat(fd, &st) < 0) {
+ *error_r = t_strdup_printf("fstat(%s) failed: %m", path);
+ i_close_fd(&fd);
+ return -1;
+ }
+ size_t value_path_len = strlen(value_path);
+ char *buf = p_malloc(pool, value_path_len + 1 + st.st_size + 1);
+ memcpy(buf, value_path, value_path_len);
+ buf[value_path_len] = '\n';
+
+ int ret = read_full(fd, buf + value_path_len + 1, st.st_size);
+ i_close_fd(&fd);
+ if (ret < 0) {
+ *error_r = t_strdup_printf("read(%s) failed: %m", path);
+ return -1;
+ }
+ if (ret == 0) {
+ *error_r = t_strdup_printf(
+ "read(%s) failed: Unexpected EOF", path);
+ return -1;
+ }
+ if (memchr(buf + value_path_len + 1, '\0', st.st_size) != NULL) {
+ *error_r = t_strdup_printf(
+ "%s contains NUL characters - This is not supported",
+ path);
+ return -1;
+ }
+
+ *output_r = buf;
+ return 0;
+}
+
static void
settings_parse_strlist(struct setting_parser_context *ctx,
ARRAY_TYPE(const_string) *array,
}
+void settings_file_get(const char *value, pool_t path_pool,
+ struct settings_file *file_r)
+{
+ const char *p;
+
+ if (*value == '\0') {
+ file_r->path = "";
+ file_r->content = "";
+ return;
+ }
+
+ p = strchr(value, '\n');
+ if (p == NULL)
+ i_panic("Settings file value is missing LF");
+ file_r->path = p_strdup_until(path_pool, value, p);
+ file_r->content = p + 1;
+}
+
+const char *settings_file_get_value(pool_t pool,
+ const struct settings_file *file)
+{
+ const char *path = file->path != NULL ? file->path : "";
+ size_t path_len = strlen(path);
+ size_t content_len = strlen(file->content);
+
+ char *value = p_malloc(pool, path_len + 1 + content_len + 1);
+ memcpy(value, path, path_len);
+ value[path_len] = '\n';
+ memcpy(value + path_len + 1, file->content, content_len);
+ return value;
+}
+
static void boollist_null_terminate(ARRAY_TYPE(const_string) *array)
{
array_append_zero(array);
value = p_strdup(ctx->set_pool, value);
*((const char **)ptr) = value;
break;
+ case SET_FILE: {
+ /* Read the file directly to get the content */
+ if (get_file(ctx, dup_value, &value) < 0) {
+ /* We may be running settings_check()s in doveconf at a
+ time when the file couldn't yet be opened. To avoid
+ unnecessary errors, set the value unknown. */
+ *((const char **)ptr) = set_value_unknown;
+ return -1;
+ }
+ *((const char **)ptr) = value;
+ break;
+ }
case SET_ENUM:
/* get the available values from default string */
i_assert(ctx->info->defaults != NULL);
SET_STR, /* string with %variables */
SET_STR_NOVARS, /* string explicitly without %variables */
SET_ENUM,
+ SET_FILE, /* string: <path> [<LF> file contents] */
SET_STRLIST, /* of type ARRAY_TYPE(const_string) */
SET_BOOLLIST, /* of type ARRAY_TYPE(const_string) - guaranteed NULL-terminted */
SET_ALIAS, /* alias name for above setting definition */
SETTING_DEFINE_STRUCT_TYPE(SET_STR_NOVARS, 0, const char *, key, name, struct_name)
#define SETTING_DEFINE_STRUCT_ENUM(key, name, struct_name) \
SETTING_DEFINE_STRUCT_TYPE(SET_ENUM, 0, const char *, key, name, struct_name)
+#define SETTING_DEFINE_STRUCT_FILE(key, name, struct_name) \
+ SETTING_DEFINE_STRUCT_TYPE(SET_FILE, 0, const char *, key, name, struct_name)
#define SETTING_DEFINE_STRUCT_BOOLLIST(key, name, struct_name) \
SETTING_DEFINE_STRUCT_TYPE(SET_BOOLLIST, 0, ARRAY_TYPE(const_string), key, name, struct_name)
#define SETTING_DEFINE_STRUCT_STRLIST(key, name, struct_name) \
SETTING_DEFINE_STRUCT_TYPE(SET_STR_NOVARS, SET_FLAG_HIDDEN, const char *, key, name, struct_name)
#define SETTING_DEFINE_STRUCT_ENUM_HIDDEN(key, name, struct_name) \
SETTING_DEFINE_STRUCT_TYPE(SET_ENUM, SET_FLAG_HIDDEN, const char *, key, name, struct_name)
+#define SETTING_DEFINE_STRUCT_FILE_HIDDEN(key, name, struct_name) \
+ SETTING_DEFINE_STRUCT_TYPE(SET_FILE, SET_FLAG_HIDDEN, const char *, key, name, struct_name)
#define SETTING_DEFINE_STRUCT_BOOLLIST_HIDDEN(key, name, struct_name) \
SETTING_DEFINE_STRUCT_TYPE(SET_BOOLLIST, SET_FLAG_HIDDEN, ARRAY_TYPE(const_string), key, name, struct_name)
#define SETTING_DEFINE_STRUCT_STRLIST_HIDDEN(key, name, struct_name) \
SETTING_DEFINE_STRUCT_TYPE(SET_STRLIST, SET_FLAG_HIDDEN, ARRAY_TYPE(const_string), key, name, struct_name)
+struct settings_file {
+ /* Path to the file. May be "" if the content is inlined. */
+ const char *path;
+ /* File contents - always available. NULs inside the file are not
+ supported. */
+ const char *content;
+};
+
struct setting_keyvalue {
const char *key;
const char *value;
bool settings_check(struct event *event, const struct setting_parser_info *info,
pool_t pool, void *set, const char **error_r);
+/* Read a SET_FILE from the given path and write "value_path\ncontents" to
+ output_r. Returns 0 on success, -1 on error. */
+int settings_parse_read_file(const char *path, const char *value_path,
+ pool_t pool,
+ const char **output_r, const char **error_r);
int settings_parse_boollist_string(const char *value, pool_t pool,
ARRAY_TYPE(const_string) *dest,
const char **error_r);
checks to try to make sure it's used only for boollists. */
const char *const *settings_boollist_get(const ARRAY_TYPE(const_string) *array);
+/* Split the settings value into path and content. The path is allocated from
+ the path_pool, while content points directly to the value string. */
+void settings_file_get(const char *value, pool_t path_pool,
+ struct settings_file *file_r);
+/* Convert settings_file into a value (path LF content). The file path may be
+ NULL, but the content must exist. */
+const char *settings_file_get_value(pool_t pool,
+ const struct settings_file *file);
+
/* Return section name escaped */
const char *settings_section_escape(const char *name);
const char *settings_section_unescape(const char *name);
const char *list_key, const char *value,
const char **error_r)
{
+ struct settings_file file;
const char *key = ctx->info->defines[key_idx].key;
const char *orig_value = value;
if (list_key != NULL)
ctx->info->defines[key_idx].type != SET_FILTER_ARRAY) {
const char *error;
str_truncate(ctx->str, 0);
+ if (ctx->info->defines[key_idx].type == SET_FILE) {
+ settings_file_get(value, &ctx->mpool->pool, &file);
+ /* Make sure only the file path is var-expanded. */
+ value = file.path;
+ }
if (var_expand_with_arrays(ctx->str, value, ctx->tables,
ctx->func_tables, ctx->func_contexts,
&error) <= 0 &&
key, error);
return -1;
}
- if (strcmp(value, str_c(ctx->str)) != 0)
+ if (strcmp(value, str_c(ctx->str)) == 0) {
+ /* unchanged value */
+ if (ctx->info->defines[key_idx].type == SET_FILE) {
+ /* Restore full SET_FILE value */
+ value = settings_file_get_value(&ctx->mpool->pool, &file);
+ }
+ } else if (ctx->info->defines[key_idx].type == SET_FILE) {
+ file.path = p_strdup(&ctx->mpool->pool, str_c(ctx->str));
+ value = settings_file_get_value(&ctx->mpool->pool, &file);
+ } else
value = p_strdup(&ctx->mpool->pool, str_c(ctx->str));
}
/* value points to mmap()ed memory, which is kept