# 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:
}
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);
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);
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 {
#include "query.h"
#include "policy_tokens.h"
+#include "str.h"
bool query_parse(query_t *query, char *p)
{
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;
+}
__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