From: Aki Tuomi Date: Fri, 18 Nov 2016 07:39:46 +0000 (+0200) Subject: lib: Add extension support for var-expand X-Git-Tag: 2.2.29.rc1~49 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=122014ad84488d6309dc7d1572dfc988fedc18c4;p=thirdparty%2Fdovecot%2Fcore.git lib: Add extension support for var-expand This enables loading new var-expand features using plugins. --- diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 8c34c3f7b6..b6cde6c588 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -292,6 +292,7 @@ headers = \ utc-offset.h \ utc-mktime.h \ var-expand.h \ + var-expand-private.h \ wildcard-match.h \ write-full.h diff --git a/src/lib/lib.c b/src/lib/lib.c index c66f64b27a..04e577c54f 100644 --- a/src/lib/lib.c +++ b/src/lib/lib.c @@ -7,6 +7,7 @@ #include "fd-close-on-exec.h" #include "ipwd.h" #include "process-title.h" +#include "var-expand-private.h" #include #include @@ -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 index 0000000000..e7a6ed361b --- /dev/null +++ b/src/lib/var-expand-private.h @@ -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 diff --git a/src/lib/var-expand.c b/src/lib/var-expand.c index 1c50a9f554..d3d2b156d0 100644 --- a/src/lib/var-expand.c +++ b/src/lib/var-expand.c @@ -13,6 +13,7 @@ #include "str.h" #include "strescape.h" #include "var-expand.h" +#include "var-expand-private.h" #include #include @@ -20,22 +21,12 @@ #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; /* [.][] */ 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); + } + } + } }