--- /dev/null
+#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
#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)
}
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,
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);
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) {
}
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') {
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;
}
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);
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 == '-') {
/* 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);
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);
+ }
+ }
+ }
}