+ return false;
+}
+
+static bool config_build_structure(config_t *config)
+{
+ bool ok = true;
+ if (config->filters.len > 0) {
+# define QSORT_TYPE filter_t
+# define QSORT_BASE config->filters.data
+# define QSORT_NELT config->filters.len
+# define QSORT_LT(a,b) strcmp(a->name, b->name) < 0
+# include "qsort.c"
+ }
+
+ foreach (filter_t *filter, config->filters) {
+ if (!filter_update_references(filter, &config->filters)) {
+ ok = false;
+ break;
+ }
+ }}
+ if (!ok) {
+ return false;
+ }
+ if (!filter_check_safety(&config->filters)) {
+ return false;
+ }
+
+ ok = false;
+#define PARSE_CHECK(Expr, Fmt, ...) \
+ if (!(Expr)) { \
+ err(Fmt, ##__VA_ARGS__); \
+ return false; \
+ }
+ foreach (filter_param_t *param, config->params) {
+ switch (param->type) {
+#define CASE(Param, State) \
+ case ATK_ ## Param ## _FILTER: \
+ ok = true; \
+ config->entry_points[SMTP_ ## State] \
+ = filter_find_with_name(&config->filters, param->value); \
+ PARSE_CHECK(config->entry_points[SMTP_ ## State] >= 0, \
+ "invalid filter name %s", param->value); \
+ break;
+ CASE(CLIENT, CONNECT)
+ CASE(EHLO, EHLO)
+ CASE(HELO, HELO)
+ CASE(SENDER, MAIL)
+ CASE(RECIPIENT, RCPT)
+ CASE(DATA, DATA)
+ CASE(END_OF_DATA, END_OF_MESSAGE)
+ CASE(VERIFY, VRFY)
+ CASE(ETRN, ETRN)
+#undef CASE
+ FILTER_PARAM_PARSE_INT(PORT, config->port);
+ FILTER_PARAM_PARSE_STRING(LOG_FORMAT, config->log_format, true);
+ default: break;
+ }
+ }}
+ array_deep_wipe(config->params, filter_params_wipe);
+
+ if (config->log_format && !query_format_check(config->log_format)) {
+ err("invalid log format: \"%s\"", config->log_format);
+ return false;
+ }
+
+ if (!ok) {
+ err("no entry point defined");
+ }
+ return ok;
+}
+
+static bool config_build_filters(config_t *config)
+{
+ foreach (filter_t *filter, config->filters) {
+ if (!filter_build(filter)) {
+ return false;
+ }
+ }}
+
+ return true;
+}
+
+static bool config_load(config_t *config) {
+ config_close(config);
+
+ if (!config_parse(config)) {
+ err("Invalid configuration: cannot parse configuration file \"%s\"", config->filename);
+ return false;
+ }
+ if (!config_build_structure(config)) {
+ err("Invalid configuration: inconsistent filter structure");
+ return false;
+ }
+ if (!config_build_filters(config)) {
+ err("Invalid configuration: invalid filter");
+ return false;
+ }
+
+ resource_garbage_collect();
+ return true;
+}
+
+bool config_reload(config_t *config)
+{
+ return config_load(config);
+}
+
+config_t *config_read(const char *file)
+{
+ config_t *config = config_new();
+ config->filename = file;
+ if (!config_reload(config)) {
+ config_delete(&config);
+ return NULL;
+ }
+ return config;
+}
+
+bool config_check(const char *file)
+{
+ config_t *config = config_new();
+ config->filename = file;
+
+ bool ret = config_parse(config) && config_build_structure(config);
+
+ config_delete(&config);
+ return ret;