dovecot-2.2: dict-sql: Implemented support for binary fields.
dovecot at dovecot.org
dovecot at dovecot.org
Wed Sep 2 16:47:43 UTC 2015
details: http://hg.dovecot.org/dovecot-2.2/rev/0453c466c601
changeset: 19073:0453c466c601
user: Timo Sirainen <tss at iki.fi>
date: Wed Sep 02 19:46:36 2015 +0300
description:
dict-sql: Implemented support for binary fields.
Example:
map {
pattern = shared/blobtest/$key
table = blobtest
value_field = value
value_hexblob = yes
fields {
key = ${hexblob:key}
}
}
Now you can access both field and value as hex data. For example:
doveadm dict set proxy::sqldict shared/blobtest/746573746b6579 7465737476616c7565
This adds "testkey" and "testvalue" to key and value fields blobs.
diffstat:
src/lib-dict/dict-sql-settings.c | 18 +++-
src/lib-dict/dict-sql-settings.h | 8 +-
src/lib-dict/dict-sql.c | 161 ++++++++++++++++++++++++++++++--------
3 files changed, 146 insertions(+), 41 deletions(-)
diffs (truncated from 427 to 300 lines):
diff -r 113f4e32cb19 -r 0453c466c601 src/lib-dict/dict-sql-settings.c
--- a/src/lib-dict/dict-sql-settings.c Wed Sep 02 19:43:26 2015 +0300
+++ b/src/lib-dict/dict-sql-settings.c Wed Sep 02 19:46:36 2015 +0300
@@ -15,7 +15,8 @@
};
struct dict_sql_map_field {
- const char *sql_field, *variable;
+ struct dict_sql_field sql_field;
+ const char *variable;
};
struct setting_parser_ctx {
@@ -28,12 +29,14 @@
};
#define DEF_STR(name) DEF_STRUCT_STR(name, dict_sql_map)
+#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, dict_sql_map)
static const struct setting_def dict_sql_map_setting_defs[] = {
DEF_STR(pattern),
DEF_STR(table),
DEF_STR(username_field),
DEF_STR(value_field),
+ DEF_BOOL(value_hexblob),
{ 0, NULL, 0 }
};
@@ -148,6 +151,7 @@
struct setting_parser_ctx *ctx)
{
struct dict_sql_map_field *field;
+ unsigned int value_len;
switch (ctx->type) {
case SECTION_ROOT:
@@ -166,8 +170,16 @@
key, NULL);
}
field = array_append_space(&ctx->cur_fields);
- field->sql_field = p_strdup(ctx->pool, key);
- field->variable = p_strdup(ctx->pool, value + 1);
+ field->sql_field.name = p_strdup(ctx->pool, key);
+ value_len = strlen(value);
+ if (strncmp(value, "${hexblob:", 10) == 0 &&
+ value[value_len-1] == '}') {
+ field->variable = p_strndup(ctx->pool, value + 10,
+ value_len-10-1);
+ field->sql_field.value_is_hexblob = TRUE;
+ } else {
+ field->variable = p_strdup(ctx->pool, value + 1);
+ }
return NULL;
}
return t_strconcat("Unknown setting: ", key, NULL);
diff -r 113f4e32cb19 -r 0453c466c601 src/lib-dict/dict-sql-settings.h
--- a/src/lib-dict/dict-sql-settings.h Wed Sep 02 19:43:26 2015 +0300
+++ b/src/lib-dict/dict-sql-settings.h Wed Sep 02 19:46:36 2015 +0300
@@ -1,6 +1,11 @@
#ifndef DICT_SQL_SETTINGS_H
#define DICT_SQL_SETTINGS_H
+struct dict_sql_field {
+ const char *name;
+ bool value_is_hexblob;
+};
+
struct dict_sql_map {
/* pattern is in simplified form: all variables are stored as simple
'$' character. fields array is sorted by the variable index. */
@@ -8,8 +13,9 @@
const char *table;
const char *username_field;
const char *value_field;
+ bool value_hexblob;
- ARRAY_TYPE(const_string) sql_fields;
+ ARRAY(struct dict_sql_field) sql_fields;
};
struct dict_sql_settings {
diff -r 113f4e32cb19 -r 0453c466c601 src/lib-dict/dict-sql.c
--- a/src/lib-dict/dict-sql.c Wed Sep 02 19:43:26 2015 +0300
+++ b/src/lib-dict/dict-sql.c Wed Sep 02 19:46:36 2015 +0300
@@ -3,6 +3,7 @@
#include "lib.h"
#include "array.h"
#include "istream.h"
+#include "hex-binary.h"
#include "str.h"
#include "sql-api-private.h"
#include "sql-db-cache.h"
@@ -211,12 +212,46 @@
}
static void
+sql_dict_value_escape(string_t *str, struct sql_dict *dict,
+ bool value_is_hexblob, const char *field_name,
+ const char *value, const char *value_suffix)
+{
+ buffer_t *buf;
+
+ if (!value_is_hexblob) {
+ str_printfa(str, "'%s%s'", sql_escape_string(dict->db, value),
+ value_suffix);
+ return;
+ }
+
+ buf = buffer_create_dynamic(pool_datastack_create(), strlen(value)/2);
+ if (hex_to_binary(value, buf) < 0) {
+ /* we shouldn't get untrusted input here. it's also a bit
+ annoying to handle this error. */
+ i_fatal("dict-sql: field %s value isn't hexblob: %s",
+ field_name, value);
+ }
+ str_append(buf, value_suffix);
+ str_append(str, sql_escape_blob(dict->db, buf->data, buf->used));
+}
+
+static void
+sql_dict_field_escape_value(string_t *str, struct sql_dict *dict,
+ const struct dict_sql_field *field,
+ const char *value, const char *value_suffix)
+{
+ sql_dict_value_escape(str, dict, field->value_is_hexblob,
+ field->name, value, value_suffix);
+}
+
+static void
sql_dict_where_build(struct sql_dict *dict, const struct dict_sql_map *map,
const ARRAY_TYPE(const_string) *values_arr,
char key1, enum sql_recurse_type recurse_type,
string_t *query)
{
- const char *const *sql_fields, *const *values;
+ const struct dict_sql_field *sql_fields;
+ const char *const *values;
unsigned int i, count, count2, exact_count;
bool priv = key1 == DICT_PATH_PRIVATE[0];
@@ -236,8 +271,8 @@
for (i = 0; i < exact_count; i++) {
if (i > 0)
str_append(query, " AND");
- str_printfa(query, " %s = '%s'", sql_fields[i],
- sql_escape_string(dict->db, values[i]));
+ str_printfa(query, " %s = ", sql_fields[i].name);
+ sql_dict_field_escape_value(query, dict, &sql_fields[i], values[i], "");
}
switch (recurse_type) {
case SQL_DICT_RECURSE_NONE:
@@ -246,24 +281,26 @@
if (i > 0)
str_append(query, " AND");
if (i < count2) {
- str_printfa(query, " %s LIKE '%s/%%' AND "
- "%s NOT LIKE '%s/%%/%%'",
- sql_fields[i],
- sql_escape_string(dict->db, values[i]),
- sql_fields[i],
- sql_escape_string(dict->db, values[i]));
+ str_printfa(query, " %s LIKE ", sql_fields[i].name);
+ sql_dict_field_escape_value(query, dict, &sql_fields[i],
+ values[i], "/%");
+ str_printfa(query, " AND %s NOT LIKE ", sql_fields[i].name);
+ sql_dict_field_escape_value(query, dict, &sql_fields[i],
+ values[i], "/%/%");
} else {
str_printfa(query, " %s LIKE '%%' AND "
"%s NOT LIKE '%%/%%'",
- sql_fields[i], sql_fields[i]);
+ sql_fields[i].name, sql_fields[i].name);
}
break;
case SQL_DICT_RECURSE_FULL:
if (i < count2) {
if (i > 0)
str_append(query, " AND");
- str_printfa(query, " %s LIKE '%s/%%'", sql_fields[i],
- sql_escape_string(dict->db, values[i]));
+ str_printfa(query, " %s LIKE ",
+ sql_fields[i].name);
+ sql_dict_field_escape_value(query, dict, &sql_fields[i],
+ values[i], "/%");
}
break;
}
@@ -275,13 +312,14 @@
}
}
-static int sql_lookup_get_query(struct sql_dict *dict, const char *key,
- string_t *query)
+static int
+sql_lookup_get_query(struct sql_dict *dict, const char *key,
+ string_t *query, const struct dict_sql_map **map_r)
{
const struct dict_sql_map *map;
ARRAY_TYPE(const_string) values;
- map = sql_dict_find_map(dict, key, &values);
+ map = *map_r = sql_dict_find_map(dict, key, &values);
if (map == NULL) {
i_error("sql dict lookup: Invalid/unmapped key: %s", key);
return -1;
@@ -293,17 +331,54 @@
return 0;
}
+static const char *
+sql_dict_result_unescape(bool hexblob, pool_t pool, struct sql_result *result,
+ unsigned int result_idx)
+{
+ const unsigned char *data;
+ size_t size;
+ string_t *str;
+
+ if (!hexblob)
+ return p_strdup(pool, sql_result_get_field_value(result, result_idx));
+
+ data = sql_result_get_field_value_binary(result, result_idx, &size);
+ str = str_new(pool, size*2 + 1);
+ binary_to_hex_append(str, data, size);
+ return str_c(str);
+}
+
+static const char *
+sql_dict_result_unescape_value(const struct dict_sql_map *map, pool_t pool,
+ struct sql_result *result)
+{
+ return sql_dict_result_unescape(map->value_hexblob, pool, result, 0);
+}
+
+static const char *
+sql_dict_result_unescape_field(const struct dict_sql_map *map, pool_t pool,
+ struct sql_result *result, unsigned int result_idx,
+ unsigned int field_idx)
+{
+ const struct dict_sql_field *sql_field;
+
+ sql_field = array_idx(&map->sql_fields, field_idx);
+ return sql_dict_result_unescape(sql_field->value_is_hexblob, pool,
+ result, result_idx);
+}
+
static int sql_dict_lookup(struct dict *_dict, pool_t pool,
const char *key, const char **value_r)
{
struct sql_dict *dict = (struct sql_dict *)_dict;
+ const struct dict_sql_map *map;
struct sql_result *result = NULL;
int ret;
T_BEGIN {
string_t *query = t_str_new(256);
- ret = sql_lookup_get_query(dict, key, query);
+ ret = sql_lookup_get_query(dict, key, query, &map);
if (ret == 0)
result = sql_query_s(dict->db, str_c(query));
} T_END;
@@ -321,8 +396,7 @@
}
*value_r = NULL;
} else {
- *value_r =
- p_strdup(pool, sql_result_get_field_value(result, 0));
+ *value_r = sql_dict_result_unescape_value(map, pool, result);
}
sql_result_unref(result);
@@ -330,6 +404,7 @@
}
struct sql_dict_lookup_context {
+ const struct dict_sql_map *map;
dict_lookup_callback_t *callback;
void *context;
};
@@ -344,8 +419,10 @@
result.ret = sql_result_next_row(sql_result);
if (result.ret < 0)
result.error = sql_result_get_error(sql_result);
- else if (result.ret > 0)
- result.value = sql_result_get_field_value(sql_result, 0);
+ else if (result.ret > 0) {
+ result.value = sql_dict_result_unescape_value(ctx->map,
+ pool_datastack_create(), sql_result);
+ }
ctx->callback(&result, ctx->context);
i_free(ctx);
@@ -356,15 +433,17 @@
dict_lookup_callback_t *callback, void *context)
{
struct sql_dict *dict = (struct sql_dict *)_dict;
+ const struct dict_sql_map *map;
struct sql_dict_lookup_context *ctx;
T_BEGIN {
More information about the dovecot-cvs
mailing list