]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: Add extension support for var-expand
authorAki Tuomi <aki.tuomi@dovecot.fi>
Fri, 18 Nov 2016 07:39:46 +0000 (09:39 +0200)
committerAki Tuomi <aki.tuomi@dovecot.fi>
Fri, 31 Mar 2017 11:03:12 +0000 (14:03 +0300)
This enables loading new var-expand features
using plugins.

src/lib/Makefile.am
src/lib/lib.c
src/lib/var-expand-private.h [new file with mode: 0644]
src/lib/var-expand.c

index 8c34c3f7b6cbac28c71c72d077c0293cda862045..b6cde6c588c7a4daaa5fb88a61db8d62f4dfad42 100644 (file)
@@ -292,6 +292,7 @@ headers = \
        utc-offset.h \
        utc-mktime.h \
        var-expand.h \
+       var-expand-private.h \
        wildcard-match.h \
        write-full.h
 
index c66f64b27afc327a772782de791ef7fa18237ca8..04e577c54f7fc7e173ef48c9e4213940536514c1 100644 (file)
@@ -7,6 +7,7 @@
 #include "fd-close-on-exec.h"
 #include "ipwd.h"
 #include "process-title.h"
+#include "var-expand-private.h"
 
 #include <fcntl.h>
 #include <unistd.h>
@@ -168,6 +169,7 @@ void lib_init(void)
        data_stack_init();
        hostpid_init();
        lib_open_non_stdio_dev_null();
+       var_expand_extensions_init();
 
        lib_initialized = TRUE;
 }
@@ -184,6 +186,7 @@ void lib_deinit(void)
        lib_atexit_run();
        ipwd_deinit();
        hostpid_deinit();
+       var_expand_extensions_deinit();
        i_close_fd(&dev_null_fd);
        data_stack_deinit();
        env_deinit();
diff --git a/src/lib/var-expand-private.h b/src/lib/var-expand-private.h
new file mode 100644 (file)
index 0000000..e7a6ed3
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef VAR_EXPAND_PRIVATE_H
+#define VAR_EXPAND_PRIVATE_H 1
+
+struct var_expand_context {
+       /* current variables */
+       const struct var_expand_table *table;
+       /* caller provided function table */
+       const struct var_expand_func_table *func_table;
+       /* caller provided context */
+       void *context;
+       /* last offset, negative counts from end*/
+       int offset;
+       /* last width, negative counts from end */
+       int width;
+       /* last zero padding */
+       bool zero_padding:1;
+};
+
+/* this can be used to register a *global* function that is
+   prepended to function table. These can be used to register some
+   special handling for keys.
+
+   you can call var_expand_with_funcs if you need to
+   expand something inside here.
+
+   return -1 on error, 0 on unknown variable, 1 on success
+*/
+typedef int
+var_expand_extension_func_t(struct var_expand_context *ctx,
+                           const char *key, const char *field,
+                           const char **result_r, const char **error_r);
+
+struct var_expand_extension_func_table {
+       const char *key;
+       var_expand_extension_func_t *func;
+};
+
+int var_expand_long(struct var_expand_context *ctx,
+                   const void *key_start, size_t key_len,
+                   const char **var_r, const char **error_r);
+
+void var_expand_extensions_init(void);
+void var_expand_extensions_deinit(void);
+
+/* Functions registered here are placed before in-built functions,
+   so you can include your own implementation of something.
+   Be careful. Use NULL terminated list.
+*/
+void var_expand_register_func_array(const struct var_expand_extension_func_table *funcs);
+void var_expand_unregister_func_array(const struct var_expand_extension_func_table *funcs);
+
+#endif
index 1c50a9f554f328c23c9331806db11de22375c0c5..d3d2b156d044110fd56df00fdc6edb3303fa75f5 100644 (file)
@@ -13,6 +13,7 @@
 #include "str.h"
 #include "strescape.h"
 #include "var-expand.h"
+#include "var-expand-private.h"
 
 #include <unistd.h>
 #include <ctype.h>
 #define TABLE_LAST(t) \
        ((t)->key == '\0' && (t)->long_key == NULL)
 
-struct var_expand_context {
-       int offset;
-       int width;
-       bool zero_padding;
-};
-
 struct var_expand_modifier {
        char key;
        const char *(*func)(const char *, struct var_expand_context *);
 };
 
-static int
-var_expand_long(const struct var_expand_table *table,
-               const struct var_expand_func_table *func_table,
-               const void *key_start, size_t key_len, void *context,
-               const char **var_r, const char **error_r);
+static ARRAY(struct var_expand_extension_func_table) var_expand_extensions;
 
 static const char *
 m_str_lcase(const char *str, struct var_expand_context *ctx ATTR_UNUSED)
@@ -195,10 +186,9 @@ var_expand_short(const struct var_expand_table *table, char key)
 }
 
 static int
-var_expand_hash(const struct var_expand_table *table,
-               const struct var_expand_func_table *func_table,
-               const char *key, const char *field, void *context,
-               const char **result_r, const char **error_r)
+var_expand_hash(struct var_expand_context *ctx,
+               const char *key, const char *field,
+               const char **result_r, const char **error_r)
 {
        enum {
                FORMAT_HEX,
@@ -228,9 +218,10 @@ var_expand_hash(const struct var_expand_table *table,
        string_t *salt = t_str_new(64);
        string_t *tmp = t_str_new(method->digest_size);
 
-       if ((ret = var_expand_long(table, func_table, field, strlen(field),
-                                  context, &value, error_r)) < 1)
+       if ((ret = var_expand_long(ctx, field, strlen(field),
+                                  &value, error_r)) < 1) {
                return ret;
+       }
 
        str_append(field_value, value);
 
@@ -277,8 +268,8 @@ var_expand_hash(const struct var_expand_table *table,
                        truncbits = I_MIN(truncbits, method->digest_size*8);
                } else if (strcmp(k, "salt") == 0) {
                        str_truncate(salt, 0);
-                       var_expand_with_funcs(salt, value, table,
-                                             func_table, context);
+                       var_expand_with_funcs(salt, value, ctx->table,
+                                                 ctx->func_table, ctx->context);
                        break;
                } else if (strcmp(k, "format") == 0) {
                        if (strcmp(value, "hex") == 0) {
@@ -374,17 +365,42 @@ var_expand_func(const struct var_expand_func_table *func_table,
 }
 
 static int
-var_expand_long(const struct var_expand_table *table,
-               const struct var_expand_func_table *func_table,
-               const void *key_start, size_t key_len, void *context,
+var_expand_try_extension(struct var_expand_context *ctx,
+                        const char *key, const char *data,
+                        const char **var_r, const char **error_r)
+{
+       int ret;
+       const char *sep = strchr(key, ';');
+
+       if (sep == NULL) sep = key + strlen(key);
+
+       /* try with extensions */
+       const struct var_expand_extension_func_table *f;
+       array_foreach(&var_expand_extensions, f) {
+               /* ensure we won't match abbreviations */
+               size_t len = sep-key;
+               if (strncasecmp(key, f->key, len) == 0 && f->key[len] == '\0')
+                       return f->func(ctx, key, data, var_r, error_r);
+       }
+       if ((ret = var_expand_func(ctx->func_table, key, data,
+                                  ctx->context, var_r, error_r)) == 0) {
+               *error_r = t_strdup_printf("Unknown variable '%%%s'", key);
+       }
+       return ret;
+}
+
+
+int
+var_expand_long(struct var_expand_context *ctx,
+               const void *key_start, size_t key_len,
                const char **var_r, const char **error_r)
 {
        const struct var_expand_table *t;
-       const char *error, *key, *value = NULL;
+       const char *key, *value = NULL;
        int ret = 1;
 
-       if (table != NULL) {
-               for (t = table; !TABLE_LAST(t); t++) {
+       if (ctx->table != NULL) {
+               for (t = ctx->table; !TABLE_LAST(t); t++) {
                        if (t->long_key != NULL &&
                            strncmp(t->long_key, key_start, key_len) == 0 &&
                            t->long_key[key_len] == '\0') {
@@ -419,18 +435,15 @@ var_expand_long(const struct var_expand_table *table,
                else
                        data = "";
 
-               /* try with hash first */
-               if ((ret = var_expand_hash(table, func_table, key,
-                                   data, context, &value, &error)) < 0) {
-                       i_error("Failed to expand %s: %s", key, error);
+               ret = var_expand_try_extension(ctx, key, data, &value, error_r);
+
+               if (ret <= 0 && value == NULL) {
                        value = "";
-               } else if (ret == 0) {
-                       return var_expand_func(func_table, key, data, context,
-                                              var_r, error_r);
                }
        }
        if (value == NULL)
-               *var_r = t_strdup_printf("UNSUPPORTED_VARIABLE_%s", key);
+               value = t_strdup_printf("UNSUPPORTED_VARIABLE_%s", key);
+       *var_r = value;
        return ret;
 }
 
@@ -449,6 +462,10 @@ void var_expand_with_funcs(string_t *dest, const char *str,
        size_t len;
 
        i_zero(&ctx);
+       ctx.table = table;
+       ctx.func_table = func_table;
+       ctx.context = context;
+
        for (; *str != '\0'; str++) {
                if (*str != '%')
                        str_append_c(dest, *str);
@@ -456,7 +473,11 @@ void var_expand_with_funcs(string_t *dest, const char *str,
                        int sign = 1;
 
                        str++;
-                       i_zero(&ctx);
+
+                       /* reset per-field modifiers */
+                       ctx.offset = 0;
+                       ctx.width = 0;
+                       ctx.zero_padding = FALSE;
 
                        /* [<offset>.]<width>[<modifiers>]<variable> */
                        if (*str == '-') {
@@ -534,8 +555,7 @@ void var_expand_with_funcs(string_t *dest, const char *str,
                                /* if there is no } it will consume rest of the
                                   string */
                                len = end - (str + 1);
-                               if (var_expand_long(table, func_table, str+1,
-                                                   len, context, &var, &error) < 0)
+                               if (var_expand_long(&ctx, str+1, len, &var, &error) < 0)
                                        i_error("var_expand_long(%s) failed: %s",
                                                str+1, error);
                                i_assert(var != NULL);
@@ -684,29 +704,56 @@ bool var_has_key(const char *str, char key, const char *long_key)
        return FALSE;
 }
 
-const struct var_expand_table *
-var_expand_table_build(char key, const char *value, char key2, ...)
+void var_expand_extensions_deinit(void)
+{
+       array_free(&var_expand_extensions);
+}
+
+void var_expand_extensions_init(void)
+{
+       i_array_init(&var_expand_extensions, 32);
+
+       /* put all hash methods there */
+       for(const struct hash_method **meth = hash_methods;
+           *meth != NULL;
+           meth++) {
+               struct var_expand_extension_func_table *func =
+                       array_append_space(&var_expand_extensions);
+               func->key = (*meth)->name;
+               func->func = var_expand_hash;
+       }
+
+       /* pkcs5 */
+       struct var_expand_extension_func_table *func =
+               array_append_space(&var_expand_extensions);
+       func->key = "pkcs5";
+       func->func = var_expand_hash;
+}
+
+void
+var_expand_register_func_array(const struct var_expand_extension_func_table *funcs)
 {
-       ARRAY(struct var_expand_table) variables;
-       struct var_expand_table *var;
-       va_list args;
-
-       i_assert(key != '\0');
-
-       t_array_init(&variables, 16);
-       var = array_append_space(&variables);
-       var->key = key;
-       var->value = value;
-
-       va_start(args, key2);
-       for (key = key2; key != '\0'; key = va_arg(args, int)) {
-               var = array_append_space(&variables);
-               var->key = key;
-               var->value = va_arg(args, const char *);
+       for(const struct var_expand_extension_func_table *ptr = funcs;
+           ptr->key != NULL;
+           ptr++) {
+               i_assert(*ptr->key != '\0');
+               array_insert(&var_expand_extensions, 0, ptr, 1);
        }
-       va_end(args);
+}
 
-       /* 0, NULL entry */
-       array_append_zero(&variables);
-       return array_idx(&variables, 0);
+void
+var_expand_unregister_func_array(const struct var_expand_extension_func_table *funcs)
+{
+       for(const struct var_expand_extension_func_table *ptr = funcs;
+           ptr->key != NULL;
+           ptr++) {
+               i_assert(ptr->func != NULL);
+               for(unsigned int i = 0; i < array_count(&var_expand_extensions); i++) {
+                       const struct var_expand_extension_func_table *func =
+                               array_idx(&var_expand_extensions, i);
+                       if (strcasecmp(func->key, ptr->key)) {
+                               array_delete(&var_expand_extensions, i, 1);
+                       }
+               }
+       }
 }