pfix-srsd: add a -I option
[apps/pfixtools.git] / postlicyd / config.c
index f2b8dbe..75f7cc2 100644 (file)
 /*     products derived from this software without specific prior written     */
 /*     permission.                                                            */
 /*                                                                            */
-/*  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND   */
-/*  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE     */
-/*  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR        */
-/*  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS    */
-/*  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR    */
-/*  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF      */
-/*  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS  */
-/*  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN   */
-/*  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)   */
-/*  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF    */
-/*  THE POSSIBILITY OF SUCH DAMAGE.                                           */
+/*  THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS   */
+/*  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED         */
+/*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE    */
+/*  DISCLAIMED.  IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY         */
+/*  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL        */
+/*  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS   */
+/*  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)     */
+/*  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,       */
+/*  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN  */
+/*  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE           */
+/*  POSSIBILITY OF SUCH DAMAGE.                                               */
+/*                                                                            */
+/*   Copyright (c) 2006-2008 the Authors                                      */
+/*   see AUTHORS and source files for details                                 */
 /******************************************************************************/
 
 /*
@@ -36,6 +39,7 @@
 #include "file.h"
 #include "config.h"
 #include "str.h"
+#include "resources.h"
 
 #define config_param_register(Param)
 
@@ -78,6 +82,12 @@ config_param_register("verify_filter");
 config_param_register("port");
 
 
+/* Format of the log message.
+ * The message exact format is $log: "reply"
+ */
+config_param_register("log_format");
+
+
 static config_t *global_config = NULL;
 
 static inline config_t *config_new(void)
@@ -94,6 +104,7 @@ static void config_close(config_t *config)
     }
     array_deep_wipe(config->filters, filter_wipe);
     array_deep_wipe(config->params, filter_params_wipe);
+    p_delete(&config->log_format);
 }
 
 void config_delete(config_t **config)
@@ -113,70 +124,8 @@ static void config_exit()
 }
 module_exit(config_exit);
 
-static bool config_second_pass(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);
-          default: break;
-        }
-    }}
-    array_deep_wipe(config->params, filter_params_wipe);
-
-    if (!ok) {
-        err("no entry point defined");
-    }
-
-    return ok;
-}
 
-static bool config_load(config_t *config)
+static bool config_parse(config_t *config)
 {
     filter_t filter;
     file_map_t map;
@@ -188,19 +137,18 @@ static bool config_load(config_t *config)
 
     char key[BUFSIZ];
     char value[BUFSIZ];
-    ssize_t key_len, value_len;
+    int key_len, value_len;
 
     if (!file_map_open(&map, config->filename, false)) {
         return false;
     }
 
-    config_close(config);
     filter_init(&filter);
     linep = p = map.map;
 
 #define READ_LOG(Lev, Fmt, ...)                                                \
     __log(LOG_ ## Lev, "config file %s:%d:%d: " Fmt, config->filename,         \
-           line + 1, p - linep + 1, ##__VA_ARGS__)
+           line + 1, (int)(p - linep + 1), ##__VA_ARGS__)
 #define READ_ERROR(Fmt, ...)                                                   \
     do {                                                                       \
         READ_LOG(ERR, Fmt, ##__VA_ARGS__);                                     \
@@ -388,17 +336,11 @@ read_filter:
     }
     end_of_section = true;
     READ_NEXT;
-    if (!filter_build(&filter)) {
-        READ_ERROR("invalid filter %s", filter.name);
-    }
     array_add(config->filters, filter);
     filter_init(&filter);
     goto read_section;
 
 ok:
-    if (!config_second_pass(config)) {
-        goto error;
-    }
     file_map_close(&map);
     return true;
 
@@ -413,6 +355,105 @@ error:
     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);
@@ -428,3 +469,14 @@ config_t *config_read(const char *file)
     }
     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;
+}