From c091e1809626636627a2824d6a2dfedeab3a4d24 Mon Sep 17 00:00:00 2001 From: Florent Bruneau Date: Sun, 12 Oct 2008 16:52:03 +0200 Subject: [PATCH] Allow query-format strings in answers. Signed-off-by: Florent Bruneau --- example/postlicyd.conf | 5 +++- postlicyd/filter.c | 4 +++ postlicyd/main-postlicyd.c | 25 +++++++++++++----- postlicyd/query.c | 54 ++++++++++++++++++++++++++++++++++++++ postlicyd/query.h | 7 +++++ 5 files changed, 87 insertions(+), 8 deletions(-) diff --git a/example/postlicyd.conf b/example/postlicyd.conf index 57c5966..6f3d07f 100644 --- a/example/postlicyd.conf +++ b/example/postlicyd.conf @@ -57,10 +57,13 @@ # 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: diff --git a/postlicyd/filter.c b/postlicyd/filter.c index 1ba19b5..d145f42 100644 --- a/postlicyd/filter.c +++ b/postlicyd/filter.c @@ -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); diff --git a/postlicyd/main-postlicyd.c b/postlicyd/main-postlicyd.c index 86db97a..70389bf 100644 --- a/postlicyd/main-postlicyd.c +++ b/postlicyd/main-postlicyd.c @@ -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 { diff --git a/postlicyd/query.c b/postlicyd/query.c index 01b7ca4..fe370b7 100644 --- a/postlicyd/query.c +++ b/postlicyd/query.c @@ -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; +} diff --git a/postlicyd/query.h b/postlicyd/query.h index 1443d65..61f894d 100644 --- a/postlicyd/query.h +++ b/postlicyd/query.h @@ -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 -- 2.20.1