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