Allow query-format strings in answers.
authorFlorent Bruneau <florent.bruneau@polytechnique.org>
Sun, 12 Oct 2008 14:52:03 +0000 (16:52 +0200)
committerFlorent Bruneau <florent.bruneau@polytechnique.org>
Sun, 12 Oct 2008 14:52:03 +0000 (16:52 +0200)
Signed-off-by: Florent Bruneau <florent.bruneau@polytechnique.org>
example/postlicyd.conf
postlicyd/filter.c
postlicyd/main-postlicyd.c
postlicyd/query.c
postlicyd/query.h

index 57c5966..6f3d07f 100644 (file)
 #   configuration associates an action to run to a return value name.
 #
 #   The action can be either a postfix access(5) value or a filter name. Postfix access
-#   parameters must be prefixed by 'postfix:'.
+#   parameters must be prefixed by 'postfix:'. The text argument given to a postfix reply
+#   may contain format strings to be replaced by the parameters of the query. This arguments
+#   have the following format: ${fieldname}
 #
 # eg:
 #   on_match = postfix:REJECT Blacklisted;
+#   on_fail  = postfix:450 Greylisted, see http://www.example.org/${sender_domain}.html
 #
 # Filter:
 #   Current defined filter types are:
index 1ba19b5..d145f42 100644 (file)
@@ -289,6 +289,10 @@ bool filter_add_hook(filter_t *filter, const char *name, int name_len,
     }
     hook.async   = false;
     hook.postfix = (strncmp(value, "postfix:", 8) == 0);
+    if (hook.postfix && query_format(NULL, 0, value + 8, NULL) == -1) {
+        err("invalid formatted text \"%s\"", value + 8);
+        return false;
+    }
     hook.value = m_strdup(hook.postfix ? value + 8 : value);
     hook.filter_id = -1;
     array_add(filter->hooks, hook);
index 86db97a..70389bf 100644 (file)
@@ -81,17 +81,28 @@ static bool config_refresh(void *mconfig)
     return config_reload(mconfig);
 }
 
-__attribute__((format(printf,2,0)))
-static void policy_answer(server_t *pcy, const char *fmt, ...)
+static void policy_answer(server_t *pcy, const char *message)
 {
-    va_list args;
     query_context_t *context = pcy->data;
     const query_t* query = &context->query;
 
     buffer_addstr(&pcy->obuf, "action=");
-    va_start(args, fmt);
-    buffer_addvf(&pcy->obuf, fmt, args);
-    va_end(args);
+    buffer_ensure(&pcy->obuf, m_strlen(message) + 64);
+
+    ssize_t size = array_size(pcy->obuf) - array_len(pcy->obuf);
+    ssize_t format_size = query_format(array_ptr(pcy->obuf, array_len(pcy->obuf)),
+                                       size, message, query);
+    if (format_size == -1) {
+        buffer_addstr(&pcy->obuf, message);
+    } else if (format_size > size) {
+        buffer_ensure(&pcy->obuf, format_size + 1);
+        query_format(array_ptr(pcy->obuf, array_len(pcy->obuf)),
+                     array_size(pcy->obuf) - array_len(pcy->obuf),
+                     message, query);
+        array_len(pcy->obuf) += format_size;
+    } else {
+        array_len(pcy->obuf) += format_size;
+    }
     buffer_addstr(&pcy->obuf, "\n\n");
     buffer_consume(&pcy->ibuf, query->eoq - pcy->ibuf.data);
     epoll_modify(pcy->fd, EPOLLIN | EPOLLOUT, pcy);
@@ -122,7 +133,7 @@ static const filter_t *next_filter(server_t *pcy, const filter_t *filter,
              query->sender == NULL ? "undefined" : query->sender,
              query->recipient == NULL ? "undefined" : query->recipient,
              htokens[hook->type], filter->name, hook->value);
-        policy_answer(pcy, "%s", hook->value);
+        policy_answer(pcy, hook->value);
         *ok = true;
         return NULL;
     } else {
index 01b7ca4..fe370b7 100644 (file)
@@ -36,6 +36,7 @@
 
 #include "query.h"
 #include "policy_tokens.h"
+#include "str.h"
 
 bool query_parse(query_t *query, char *p)
 {
@@ -185,6 +186,59 @@ const char *query_field_for_id(const query_t *query, postlicyd_token id)
 const char *query_field_for_name(const query_t *query, const char *name)
 {
     postlicyd_token id = policy_tokenize(name, strlen(name));
+    if (id == PTK_UNKNOWN) {
+        warn("unknown query field %s", name);
+        return NULL;
+    }
     return query_field_for_id(query, id);
 }
 
+ssize_t query_format(char *dest, size_t len, const char *fmt, const query_t *query)
+{
+    size_t written = 0;
+    size_t pos = 0;
+    const char *end = fmt + m_strlen(fmt);
+
+#define WRITE(Src, Len)                                                        \
+    do {                                                                       \
+        size_t __len     = (Len);                                              \
+        if (written < len) {                                                   \
+            size_t __to_write = MIN(len - written - 1, __len);                 \
+            memcpy(dest + written, (Src), __to_write);                         \
+            written += __to_write;                                             \
+        }                                                                      \
+        pos += __len;                                                          \
+    } while (0)
+    while (*fmt != '\0') {
+        const char *next_format = strstr(fmt, "${");
+        if (next_format == NULL) {
+            next_format = end;
+        }
+        WRITE(fmt, next_format - fmt);
+        fmt = next_format;
+        if (*fmt != '\0') {
+            fmt += 2;
+            next_format = strchr(fmt, '}');
+            if (next_format == NULL) {
+                return -1;
+            }
+
+            postlicyd_token tok = policy_tokenize(fmt, next_format - fmt);
+            if (tok == PTK_UNKNOWN) {
+                warn("unknown field name \"%.*s\"", next_format - fmt, fmt);
+            }
+            const char *field = query == NULL ? NULL : query_field_for_id(query, tok);
+            if (field == NULL) {
+                WRITE("(null)", 6);
+            } else {
+                WRITE(field, m_strlen(field));
+            }
+            fmt = next_format + 1;
+        }
+    }
+
+    if (written > 0 && len > 0) {
+        dest[written] = '\0';
+    }
+    return pos;
+}
index 1443d65..61f894d 100644 (file)
@@ -113,4 +113,11 @@ const char *query_field_for_name(const query_t *query, const char *name);
 __attribute__((nonnull))
 const char *query_field_for_id(const query_t *query, postlicyd_token id);
 
+/** Formats the given string by replacing ${field_name} with the content
+ * of the query.
+ * Unknown and empty fields are filled with (null).
+ */
+__attribute__((nonnull(3)))
+ssize_t query_format(char *dest, size_t len, const char* fmt, const query_t *query);
+
 #endif