settings-parser.c revision bb308de9d25db75528605eb733a418c996d416ad
/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "hash.h"
#include "network.h"
#include "istream.h"
#include "execv-const.h"
#include "str.h"
#include "strescape.h"
#include "var-expand.h"
#include "settings-parser.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
struct setting_link {
struct setting_link *parent;
const struct setting_parser_info *info;
const char *full_key;
/* Points to array inside parent->set_struct.
SET_DEFLIST : array of set_structs
SET_STRLIST : array of const_strings */
/* Pointer to structure containing the values */
void *set_struct;
/* Pointer to structure containing non-zero values for settings that
have been changed. */
void *change_struct;
/* SET_DEFLIST: array of change_structs */
};
struct setting_parser_context {
enum settings_parser_flags flags;
bool str_vars_are_expanded;
struct setting_link *roots;
unsigned int root_count;
struct hash_table *links;
unsigned int linenum;
const char *error;
const struct setting_parser_info *prev_info;
};
static const struct setting_parser_info strlist_info = {
.module_name = NULL,
.struct_size = 0,
};
static void
const struct setting_parser_info *info,
struct setting_link *link);
static int
const struct setting_link *src_link,
struct setting_parser_context *
enum settings_parser_flags flags)
{
}
static void
const struct setting_define *def,
struct setting_link *link)
{
struct setting_link *new_link;
struct setting_parser_info info;
void *const *children;
char *full_key;
unsigned int i, count;
if (!array_is_created(arr))
return;
}
for (i = 0; i < count; i++) {
}
}
}
static void
const struct setting_parser_info *info,
struct setting_link *link)
{
const struct setting_define *def;
const char *p, **strp;
return;
case SET_ENUM: {
/* fix enums by dropping everything after the
first ':' */
if (p != NULL)
break;
}
case SET_STR_VARS: {
/* insert the unexpanded-character */
}
break;
}
case SET_DEFLIST_UNIQUE:
break;
default:
break;
}
}
}
struct setting_parser_context *
const struct setting_parser_info *const *roots,
{
struct setting_parser_context *ctx;
unsigned int i;
for (i = 0; i < count; i++) {
if (roots[i]->struct_size == 0)
continue;
if ((flags & SETTINGS_PARSER_FLAG_TRACK_CHANGES) != 0) {
}
}
return ctx;
}
{
}
{
}
{
unsigned int i;
void **sets;
for (i = 0; i < ctx->root_count; i++)
return sets;
}
{
}
{
}
static const struct setting_define *
{
const struct setting_define *list;
return list;
}
return NULL;
}
static int
{
else {
return -1;
}
return 0;
}
static int
unsigned int *result_r)
{
int num;
/* use %i so we can handle eg. 0600 as octal value with umasks */
return -1;
}
return 0;
}
const char **error_r)
{
char *p;
while (*p == ' ') p++;
switch (i_toupper(*p)) {
case 'S':
multiply = 1;
p = "";
break;
case 'M':
multiply = 60;
p = "";
break;
case 'H':
p = "";
break;
case 'D':
p = "";
break;
case 'W':
p = "";
break;
}
if (*p != '\0') {
return -1;
}
return -1;
}
return 0;
}
const char **error_r)
{
char *p;
while (*p == ' ') p++;
switch (i_toupper(*p)) {
case 'B':
multiply = 1;
p += 1;
break;
case 'K':
multiply = 1024;
p += 1;
break;
case 'M':
p += 1;
break;
case 'G':
p += 1;
break;
case 'T':
p += 1;
break;
}
if (multiply > 1) {
/* Allow: k, ki, kiB */
if (i_toupper(*p) == 'I')
p++;
if (i_toupper(*p) == 'B')
p++;
}
if (*p != '\0') {
return -1;
}
return -1;
}
return 0;
}
char **result_r, const char *allowed_values)
{
const char *p;
while (allowed_values != NULL) {
if (p == NULL) {
break;
"Invalid value: ",
return -1;
}
break;
allowed_values = p + 1;
}
return 0;
}
static void
struct setting_link *link)
{
void *ptr;
}
}
}
const struct setting_define *def,
{
struct setting_link *link;
return 0;
" already exists", NULL);
return -1;
}
return 0;
}
static int
const struct setting_define *def,
const struct setting_parser_info *info,
{
struct setting_link new_link;
const char *const *list;
char *full_key;
if (!array_is_created(result))
if (info == &strlist_info) {
/* there are no sections below strlist, so allow referencing it
return -1;
}
if (**list == '\0')
continue;
return -1;
}
return 0;
}
static int
const struct setting_define *def,
{
const char *error;
def--;
}
case SET_BOOL:
return -1;
break;
case SET_UINT:
case SET_UINT_OCT:
return -1;
break;
case SET_TIME:
return -1;
}
break;
case SET_SIZE:
return -1;
}
break;
case SET_STR:
break;
case SET_STR_VARS:
break;
case SET_ENUM:
/* get the available values from default string */
*(const char **)ptr2) < 0)
return -1;
break;
case SET_DEFLIST:
case SET_DEFLIST_UNIQUE:
case SET_STRLIST: {
return -1;
break;
}
case SET_ALIAS:
i_unreached();
break;
}
if (change_ptr != NULL)
*((char *)change_ptr) = 1;
return 0;
}
static bool
const struct setting_define **def_r,
struct setting_link **link_r)
{
const struct setting_define *def;
struct setting_link *link;
const char *end;
unsigned int i;
/* try to find from roots */
for (i = 0; i < ctx->root_count; i++) {
return TRUE;
}
}
/* try to find from links */
return FALSE;
return FALSE;
return TRUE;
} else {
}
}
static void
struct setting_link *link,
{
void *const *items;
unsigned int i, count;
/* replace if it already exists */
for (i = 0; i < count; i += 2) {
return;
}
}
}
{
const struct setting_define *def;
struct setting_link *link;
return 1;
}
return -1;
return 1;
} else {
return 0;
}
}
const char *key)
{
const struct setting_define *def;
struct setting_link *link;
}
const void *
{
const struct setting_define *def;
struct setting_link *link;
return NULL;
return NULL;
}
const char *key)
{
const struct setting_define *def;
struct setting_link *link;
const unsigned char *p;
return NULL;
return FALSE;
return *p;
}
{
int ret;
return -1;
}
return -1;
}
T_BEGIN {
} T_END;
return ret;
}
const struct setting_parser_info *
{
}
static const char *settings_translate_lf(const char *value)
{
char *dest, *p;
return value;
for (p = dest; *p != '\0'; p++) {
if (*p == SETTING_STREAM_LF_CHAR[0])
*p = '\n';
}
return dest;
}
{
bool ignore_unknown_keys =
const char *line;
int ret;
if (*line == '\0') {
/* empty line finishes it */
return 0;
}
return -1;
}
T_BEGIN {
} T_END;
return -1;
}
}
return 1;
}
{
int ret;
do {
return -1;
if (ret == 0) {
/* empty line read */
return 0;
}
switch (ret) {
case -1:
if (input->stream_errno != 0) {
"read() failed: %m");
} else {
}
break;
case -2:
"Line %u: line too long",
break;
case 0:
/* blocks */
return 1;
default:
i_unreached();
}
return -1;
}
{
if (fd < 0) {
"open(%s) failed: %m", path);
return -1;
}
return ret;
}
{
}
{
extern char **environ;
char *const *sorted_envs;
unsigned int i, count;
int ret = 0;
return 0;
/* sort the settings first. this is necessary for putenv()
implementations (e.g. valgrind) which change the order of strings
in environ[] */
"Invalid setting %s: %s",
ret = -1;
}
} T_END;
}
return ret;
}
const char *bin_path, const char *config_path,
const char *service)
{
i_error("pipe() failed: %m");
return -1;
}
i_error("fork() failed: %m");
return -1;
}
if (pid == 0) {
/* child */
static const char *argv[] = {
NULL,
"-c", NULL,
"-p", NULL,
};
i_fatal("dup2() failed: %m");
}
i_error("waitpid() failed: %m");
ret = -1;
} else if (status != 0) {
ret = -1;
}
return ret;
}
{
const struct setting_define *def;
void *const *children;
unsigned int i, count;
return FALSE;
}
continue;
if (!array_is_created(val))
continue;
for (i = 0; i < count; i++) {
return FALSE;
}
}
return TRUE;
}
const char **error_r)
{
unsigned int i;
for (i = 0; i < ctx->root_count; i++) {
return FALSE;
}
return TRUE;
}
bool is_expanded)
{
}
{
const struct setting_define *def;
struct setting_link *link;
const char **val;
return;
/* parent is strlist, no expansion needed */
return;
}
**val == SETTING_STRVAR_EXPANDED[0]);
}
}
{
}
{
unsigned int i;
for (i = 0; i < ctx->root_count; i++) {
}
}
static void
{
const struct setting_define *def;
unsigned int i, count;
case SET_BOOL:
case SET_UINT:
case SET_UINT_OCT:
case SET_TIME:
case SET_SIZE:
case SET_STR:
case SET_ENUM:
case SET_STRLIST:
case SET_ALIAS:
break;
case SET_STR_VARS: {
break;
**val == SETTING_STRVAR_UNEXPANDED[0]);
*val += 1;
} else if (**val == SETTING_STRVAR_UNEXPANDED[0]) {
str_truncate(str, 0);
} else {
*val += 1;
}
break;
}
case SET_DEFLIST:
case SET_DEFLIST_UNIQUE: {
if (!array_is_created(val))
break;
for (i = 0; i < count; i++) {
}
break;
}
}
}
}
const struct var_expand_table *table)
{
T_BEGIN {
} T_END;
}
char var_key, const char *long_var_key,
{
const struct setting_define *def;
const void *value;
void *const *children;
unsigned int i, count;
case SET_BOOL:
case SET_UINT:
case SET_UINT_OCT:
case SET_TIME:
case SET_SIZE:
case SET_STR:
case SET_ENUM:
case SET_STRLIST:
case SET_ALIAS:
break;
case SET_STR_VARS: {
break;
if (**val == SETTING_STRVAR_UNEXPANDED[0]) {
long_var_key)) {
return TRUE;
}
} else {
}
break;
}
case SET_DEFLIST:
case SET_DEFLIST_UNIQUE: {
if (!array_is_created(val))
break;
for (i = 0; i < count; i++) {
return TRUE;
}
break;
}
}
}
return FALSE;
}
{
void **ptr;
return;
}
static bool
{
switch (type) {
case SET_BOOL: {
break;
}
case SET_UINT:
case SET_UINT_OCT:
case SET_TIME: {
break;
}
case SET_SIZE: {
break;
}
case SET_STR_VARS:
case SET_STR:
case SET_ENUM: {
break;
}
case SET_DEFLIST:
case SET_DEFLIST_UNIQUE:
return FALSE;
case SET_STRLIST: {
unsigned int i, count;
if (!array_is_created(src_arr))
break;
if (!array_is_created(dest_arr))
for (i = 0; i < count; i++) {
}
break;
}
case SET_ALIAS:
break;
}
return TRUE;
}
{
const struct setting_define *def;
const void *src;
unsigned int i, count;
if (info->struct_size == 0)
return NULL;
/* don't just copy everything from set to dest_set. it may contain
some non-setting fields allocated from the original pool. */
void *child_set;
if (!array_is_created(src_arr))
continue;
for (i = 0; i < count; i++) {
dest_set);
}
}
}
return dest_set;
}
static void *
{
const struct setting_define *def;
const void *src;
unsigned int i, count;
return NULL;
case SET_BOOL:
case SET_UINT:
case SET_UINT_OCT:
case SET_TIME:
case SET_SIZE:
case SET_STR_VARS:
case SET_STR:
case SET_ENUM:
case SET_STRLIST:
break;
case SET_DEFLIST:
case SET_DEFLIST_UNIQUE: {
void *child_set;
if (!array_is_created(src_arr))
break;
for (i = 0; i < count; i++) {
children[i],
pool);
}
break;
}
case SET_ALIAS:
break;
}
}
return dest_set;
}
static void
const struct dynamic_settings_parser *parsers)
{
/* @UNSAFE */
struct dynamic_settings_parser new_parser;
const struct setting_define *cur_defines;
void *parent_defaults;
unsigned int i, j;
/* add existing defines */
/* add new dynamic defines */
new_define = cur_defines[j];
}
}
/* update defaults */
}
/* update dynamic parsers list */
}
}
new_parser = parsers[i];
}
sizeof(*parent->dynamic_parsers) *
}
struct setting_parser_info *parent,
const struct dynamic_settings_parser *parsers)
{
} T_END;
}
{
unsigned int i;
return NULL;
return CONST_PTR_OFFSET(base_set,
}
}
return NULL;
}
static struct setting_link *
struct hash_table *links,
struct setting_link *old_link)
{
struct setting_link *new_link;
return new_link;
/* find the array from parent struct */
/* find our struct from array */
for (i = 0; i < count; i++) {
break;
}
}
}
return new_link;
}
struct setting_parser_context *
{
struct setting_parser_context *new_ctx;
struct hash_iterate_context *iter;
struct setting_link *new_link;
struct hash_table *links;
unsigned int i;
for (i = 0; i < new_ctx->root_count; i++) {
}
new_link);
}
return new_ctx;
}
static void *
{
const struct setting_define *def;
unsigned int i, count;
if (info->struct_size == 0)
return NULL;
continue;
if (array_is_created(src_arr)) {
for (i = 0; i < count; i++) {
}
}
}
return dest_set;
}
const struct setting_link *src_link,
struct setting_link *dest_link,
{
unsigned int i, count;
if (!array_is_created(src_arr))
return;
if (!array_is_created(dest_arr))
for (i = 0; i < count; i++) {
}
/* copy changes */
if (!array_is_created(dest_arr))
for (i = 0; i < count; i++) {
}
}
static int
const struct setting_link *src_link,
struct setting_link *dest_link,
{
void *const *src_children, *const *src_cchildren;
const char *const *src_namep, *const *dest_namep;
unsigned int type_offset;
if (!array_is_created(src_arr))
return 0;
if (!array_is_created(dest_arr)) {
}
for (i = 0; i < src_count; i++) {
for (j = 0; j < dest_count; j++) {
break;
}
/* merge */
pool, conflict_key_r) < 0)
return -1;
} else {
/* append */
src_children[i], pool);
src_cchildren[i],
pool);
}
}
return 0;
}
static int
const struct setting_link *src_link,
{
const struct setting_define *def;
/* just add the new values */
/* merge sections */
} else if (*((const char *)csrc) == 0) {
/* unchanged */
continue;
/* ignore aliases */
continue;
} else if (*((const char *)cdest) != 0) {
/* conflict */
if (conflict_key_r != NULL) {
return -1;
}
continue;
} else {
*((char *)cdest) = 1;
}
/* found a changed setting */
/* non-list */
} else {
conflict_key_r) < 0)
return -1;
}
}
return 0;
}
const struct setting_parser_context *src,
{
unsigned int i;
for (i = 0; i < dest->root_count; i++) {
conflict_key_r) < 0)
return -1;
}
return 0;
}
const char *settings_section_escape(const char *name)
{
#define CHAR_NEED_ESCAPE(c) \
unsigned int i;
for (i = 0; name[i] != '\0'; i++) {
if (CHAR_NEED_ESCAPE(name[i]))
break;
}
if (name[i] == '\0')
return name;
for (; name[i] != '\0'; i++) {
switch (name[i]) {
case '=':
break;
case SETTINGS_SEPARATOR:
break;
case '\\':
break;
default:
break;
}
}
}