/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "hash.h"
#include "net.h"
#include "istream.h"
#include "env-util.h"
#include "execv-const.h"
#include "str.h"
#include "strescape.h"
#include "var-expand.h"
#include "settings-parser.h"
#include <stdio.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
struct setting_link {
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 {
bool str_vars_are_expanded;
unsigned int root_count;
unsigned int linenum;
const char *error;
};
.module_name = NULL,
.struct_size = 0,
};
struct setting_link *);
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)
{
void *const *children;
char *full_key;
unsigned int i, count;
if (!array_is_created(arr))
return;
}
}
} T_END;
}
static void
const struct setting_parser_info *info,
struct setting_link *link)
{
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,
{
unsigned int i;
1024);
/* use case-insensitive comparisons. this is mainly because settings
may go through environment variables where their keys get
uppercased. of course the alternative would be to not uppercase
environment. probably doesn't make much difference which way is
chosen. */
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;
}
{
}
const struct setting_parser_info *const *
{
unsigned int i;
for (i = 0; i < ctx->root_count; i++)
return infos;
}
{
}
static const struct setting_define *
{
return list;
}
return NULL;
}
static int
{
else {
"Invalid boolean value: %s (use yes or no)", value);
return -1;
}
return 0;
}
static int
unsigned int *result_r)
{
"Invalid number %s: %s", value,
return -1;
}
return 0;
}
static int
unsigned int *result_r)
{
unsigned long long octal;
if (*value != '0')
return -1;
}
return 0;
}
bool milliseconds, const char **error_r)
{
const char *p;
return -1;
}
while (*p == ' ') p++;
if (*p == '\0' && num != 0) {
"(add e.g. 's' for seconds)", str);
return -1;
}
switch (i_toupper(*p)) {
case 'S':
multiply *= 1;
p = "";
break;
case 'M':
multiply *= 60;
p = "";
if (!milliseconds) {
/* allow ms also for seconds, as long
as it's divisible by seconds */
num /= 1000;
}
multiply = 1;
p = "";
break;
}
"Milliseconds not supported for this setting: %s", str);
return -1;
}
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)
{
}
const char **error_r)
{
}
const char **error_r)
{
const char *p;
return -1;
}
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;
}
}
}
static int ATTR_NULL(2)
const struct setting_define *def,
{
return 0;
" already exists", NULL);
return -1;
}
return 0;
}
const struct setting_define *def,
const struct setting_parser_info *info,
{
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
{
"Invalid port number %s", value);
return -1;
}
return 0;
}
static int
const struct setting_define *def,
{
const void *ptr2;
const char *error;
def--;
}
case SET_BOOL:
return -1;
break;
case SET_UINT:
return -1;
break;
case SET_UINT_OCT:
return -1;
break;
case SET_TIME:
return -1;
}
break;
case SET_TIME_MSECS:
return -1;
}
break;
case SET_SIZE:
return -1;
}
break;
case SET_IN_PORT:
return -1;
break;
case SET_STR:
break;
case SET_STR_VARS:
break;
case SET_ENUM:
/* get the available values from default string */
*(const char *const *)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
unsigned int *n, const struct setting_define **def_r,
struct setting_link **link_r)
{
unsigned int i;
/* try to find from roots */
for (i = *n; i < ctx->root_count; i++) {
*n = i + 1;
return TRUE;
}
}
if (*n > ctx->root_count)
return FALSE;
*n += 1;
/* try to find from links */
return FALSE;
/* maybe this is the first strlist value */
unsigned int parent_n = 0;
&parent_def, &parent_link))
return FALSE;
if (parent_def == NULL) {
/* we'll get here with e.g. "plugin/a/b=val".
not sure if we should ever do anything here.. */
return FALSE;
} else {
return FALSE;
}
/* setting parent_key=0 adds it to links list */
return FALSE;
}
return TRUE;
} else {
}
}
static bool
const struct setting_define **def_r,
struct setting_link **link_r)
{
unsigned int n = 0;
}
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;
}
}
}
{
unsigned int n = 0;
return 0;
}
do {
return 1;
}
return -1;
/* there may be more instances of the setting */
return 1;
}
const char *key)
{
}
const char *key)
{
return NULL;
/* strlist */
return key;
}
def--;
}
}
const void *
{
return NULL;
return NULL;
}
const char *key)
{
const unsigned char *p;
return FALSE;
return FALSE;
return *p != 0;
}
{
int ret;
return -1;
}
return -1;
}
T_BEGIN {
} T_END;
return ret;
}
const struct setting_parser_info *
{
}
{
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:
break;
if (input->stream_errno != 0) {
"read(%s) disconnected before receiving any data",
} else {
"read(%s) disconnected before receiving "
"end-of-settings line",
}
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;
}
{
}
{
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");
i_close_fd(&fd[0]);
return -1;
}
if (pid == 0) {
/* child */
static const char *argv[] = {
NULL,
"-c", NULL,
"-p", NULL,
};
i_close_fd(&fd[0]);
i_fatal("dup2() failed: %m");
}
i_error("waitpid() failed: %m");
ret = -1;
} else if (status != 0) {
ret = -1;
}
return ret;
}
static bool
{
unsigned int i;
return TRUE;
error_r))
return FALSE;
}
return TRUE;
}
{
void *const *children;
unsigned int i, count;
return FALSE;
}
continue;
if (!array_is_created(val))
continue;
for (i = 0; i < count; i++) {
return FALSE;
}
}
}
const char **error_r)
{
unsigned int i;
for (i = 0; i < ctx->root_count; i++) {
return FALSE;
}
return TRUE;
}
bool is_expanded)
{
}
{
const char **val;
return;
/* parent is strlist, no expansion needed */
return;
}
**val == SETTING_STRVAR_EXPANDED[0]);
}
}
{
}
const struct var_expand_table *table,
const struct var_expand_func_table *func_table,
const char **error_r)
{
const char *error;
unsigned int i, count;
case SET_BOOL:
case SET_UINT:
case SET_UINT_OCT:
case SET_TIME:
case SET_TIME_MSECS:
case SET_SIZE:
case SET_IN_PORT:
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);
&error);
}
} else {
*val += 1;
}
break;
}
case SET_DEFLIST:
case SET_DEFLIST_UNIQUE: {
if (!array_is_created(val))
break;
for (i = 0; i < count; i++) {
}
}
break;
}
}
}
if (final_ret <= 0)
return final_ret;
return -1;
}
return -1;
}
}
}
return final_ret;
}
const struct var_expand_table *table,
const char **error_r)
{
}
const struct var_expand_table *table,
const struct var_expand_func_table *func_table,
void *func_context, const char **error_r)
{
int ret;
T_BEGIN {
const char *error;
&error);
if (ret <= 0)
} T_END;
return ret;
}
{
unsigned int i;
const char *error;
for (i = 0; i < ctx->root_count; i++) {
&error);
}
}
char var_key, const char *long_var_key,
{
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_TIME_MSECS:
case SET_SIZE:
case SET_IN_PORT:
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
bool keep_values)
{
switch (type) {
case SET_BOOL: {
break;
}
case SET_UINT:
case SET_UINT_OCT:
case SET_TIME:
case SET_TIME_MSECS: {
break;
}
case SET_SIZE: {
break;
}
case SET_IN_PORT: {
break;
}
case SET_STR_VARS:
case SET_STR:
case SET_ENUM: {
if (keep_values)
else
break;
}
case SET_DEFLIST:
case SET_DEFLIST_UNIQUE:
return FALSE;
case SET_STRLIST: {
if (!array_is_created(src_arr))
break;
if (!array_is_created(dest_arr))
for (i = 0; i < count; i += 2) {
if (dest_count > 0) {
for (j = 0; j < dest_count; j += 2) {
break;
}
if (j < dest_count)
continue;
}
}
break;
}
case SET_ALIAS:
break;
}
return TRUE;
}
{
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 void *src;
unsigned int i, count;
return NULL;
case SET_BOOL:
case SET_UINT:
case SET_UINT_OCT:
case SET_TIME:
case SET_TIME_MSECS:
case SET_SIZE:
case SET_IN_PORT:
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 */
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;
}
static void
{
unsigned int i, count;
for (i = 0; i < count; i++) {
continue;
}
}
const struct setting_parser_info *const **_roots,
const struct dynamic_settings_parser *dyn_parsers)
{
unsigned int i, count;
/* settings_parser_info_update() modifies the parent structure.
since we may be using the same structure later, we want it to be
in its original state, so we'll have to copy all structures. */
*new_parent = *old_parent;
/* update root */
for (i = 0; i < count; i++) {
if (roots[i] == old_parent)
new_roots[i] = new_parent;
else
}
/* update parent in dyn_parsers */
for (i = 0; i < count; i++) {
new_dyn_parsers[i] = dyn_parsers[i];
}
}
{
unsigned int i;
return NULL;
return CONST_PTR_OFFSET(base_set,
}
}
return NULL;
}
static struct setting_link *
struct setting_link *old_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 *
{
char *key;
unsigned int i;
bool keep_values;
/* if source and destination pools are the same, there's no need to
duplicate values */
1024);
for (i = 0; i < new_ctx->root_count; i++) {
}
}
return new_ctx;
}
static void *
{
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,
{
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,
{
/* 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;
}
{
#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;
case ' ':
break;
case ',':
break;
default:
break;
}
}
}