Mercurial > dovecot > core-2.2
changeset 12308:22689f4ceecb
liblib: Added API for easily building hash strings based on given format string and input.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Tue, 19 Oct 2010 18:10:34 +0100 |
parents | 368fd1cce4d6 |
children | e3fe87b855ef |
files | src/lib/Makefile.am src/lib/hash-format.c src/lib/hash-format.h src/lib/test-hash-format.c src/lib/test-lib.c src/lib/test-lib.h |
diffstat | 6 files changed, 306 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib/Makefile.am Tue Oct 19 18:09:16 2010 +0100 +++ b/src/lib/Makefile.am Tue Oct 19 18:10:34 2010 +0100 @@ -36,6 +36,7 @@ file-lock.c \ file-set-size.c \ hash.c \ + hash-format.c \ hash-method.c \ hash2.c \ hex-binary.c \ @@ -146,6 +147,7 @@ file-set-size.h \ fsync-mode.h \ hash.h \ + hash-format.h \ hash-method.h \ hash2.h \ hex-binary.h \ @@ -231,6 +233,7 @@ test-bsearch-insert-pos.c \ test-buffer.c \ test-crc32.c \ + test-hash-format.c \ test-hex-binary.c \ test-istream-concat.c \ test-istream-crlf.c \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib/hash-format.c Tue Oct 19 18:10:34 2010 +0100 @@ -0,0 +1,228 @@ +/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "base64.h" +#include "hex-binary.h" +#include "str.h" +#include "hash-method.h" +#include "hash-format.h" + +enum hash_encoding { + HASH_ENCODING_HEX, + HASH_ENCODING_HEX_SHORT, + HASH_ENCODING_BASE64 +}; + +struct hash_format_list { + struct hash_format_list *next; + + const struct hash_method *method; + void *context; + unsigned int bits; + enum hash_encoding encoding; +}; + +struct hash_format { + pool_t pool; + const char *str; + + struct hash_format_list *list, **pos; +}; + +static int +hash_format_parse(const char *str, unsigned int *idxp, + const struct hash_method **method_r, + unsigned int *bits_r, const char **error_r) +{ + const char *name, *end, *bitsp; + unsigned int bits, i = *idxp; + + /* we should have "hash_name}" or "hash_name:bits}" */ + end = strchr(str+i, '}'); + if (end == NULL) { + *error_r = "Missing '}'"; + return -1; + } + *idxp = end - str; + name = t_strdup_until(str+i, end); + + bitsp = strchr(name, ':'); + if (bitsp != NULL) + name = t_strdup_until(name, bitsp++); + + *method_r = hash_method_lookup(name); + if (*method_r == NULL) { + *error_r = t_strconcat("Unknown hash method: ", name, NULL); + return -1; + } + + bits = (*method_r)->digest_size * 8; + if (bitsp != NULL) { + if (str_to_uint(bitsp, &bits) < 0 || + bits == 0 || bits > (*method_r)->digest_size*8) { + *error_r = t_strconcat("Invalid :bits number: ", + bitsp, NULL); + return -1; + } + if ((bits % 8) != 0) { + *error_r = t_strconcat( + "Currently :bits must be divisible by 8: ", + bitsp, NULL); + return -1; + } + } + *bits_r = bits; + return 0; +} + +static int +hash_format_string_analyze(struct hash_format *format, const char *str, + const char **error_r) +{ + struct hash_format_list *list; + unsigned int i; + + for (i = 0; str[i] != '\0'; i++) { + if (str[i] != '%') + continue; + i++; + + list = p_new(format->pool, struct hash_format_list, 1); + list->encoding = HASH_ENCODING_HEX; + *format->pos = list; + format->pos = &list->next; + + if (str[i] == 'B') { + list->encoding = HASH_ENCODING_BASE64; + i++; + } else if (str[i] == 'X') { + list->encoding = HASH_ENCODING_HEX_SHORT; + i++; + } + if (str[i++] != '{') { + *error_r = "No '{' after '%'"; + return -1; + } + if (hash_format_parse(str, &i, &list->method, + &list->bits, error_r) < 0) + return -1; + list->context = p_malloc(format->pool, + list->method->context_size); + list->method->init(list->context); + } + return 0; +} + +int hash_format_init(const char *format_string, struct hash_format **format_r, + const char **error_r) +{ + struct hash_format *format; + pool_t pool; + int ret; + + pool = pool_alloconly_create("hash format", 1024); + format = p_new(pool, struct hash_format, 1); + format->pool = pool; + format->str = p_strdup(pool, format_string); + format->pos = &format->list; + T_BEGIN { + ret = hash_format_string_analyze(format, format_string, + error_r); + if (ret < 0) + *error_r = p_strdup(format->pool, *error_r); + } T_END; + if (ret < 0) { + *error_r = t_strdup(*error_r); + return -1; + } + *format_r = format; + return 0; +} + +void hash_format_loop(struct hash_format *format, + const void *data, size_t size) +{ + struct hash_format_list *list; + + for (list = format->list; list != NULL; list = list->next) + list->method->loop(list->context, data, size); +} + +static void +hash_format_digest(string_t *dest, const struct hash_format_list *list, + const unsigned char *digest) +{ + unsigned int i, orig_len, size = list->bits / 8; + + i_assert(list->bits % 8 == 0); + + switch (list->encoding) { + case HASH_ENCODING_HEX: + binary_to_hex_append(dest, digest, size); + break; + case HASH_ENCODING_HEX_SHORT: + orig_len = str_len(dest); + binary_to_hex_append(dest, digest, size); + /* drop leading zeros, except if it's the only one */ + for (i = orig_len; i < str_len(dest); i++) { + if (str_data(dest)[i] != '0') + break; + } + if (i == str_len(dest)) i--; + str_delete(dest, orig_len, i-orig_len); + break; + case HASH_ENCODING_BASE64: + orig_len = str_len(dest); + base64_encode(digest, size, dest); + /* drop trailing '=' chars */ + while (str_len(dest) > orig_len && + str_data(dest)[str_len(dest)-1] == '=') + str_truncate(dest, str_len(dest)-1); + break; + } +} + +void hash_format_deinit(struct hash_format **_format, string_t *dest) +{ + struct hash_format *format = *_format; + struct hash_format_list *list; + const char *p; + unsigned char *digest; + unsigned int i, max_digest_size = 0; + + *_format = NULL; + + for (list = format->list; list != NULL; list = list->next) { + if (max_digest_size < list->method->digest_size) + max_digest_size = list->method->digest_size; + } + digest = p_malloc(format->pool, max_digest_size); + + list = format->list; + for (i = 0; format->str[i] != '\0'; i++) { + if (format->str[i] != '%') { + str_append_c(dest, format->str[i]); + continue; + } + + /* we already verified that the string is ok */ + i_assert(list != NULL); + list->method->result(list->context, digest); + hash_format_digest(dest, list, digest); + list = list->next; + + p = strchr(format->str+i, '}'); + i_assert(p != NULL); + i = p - format->str; + } + + pool_unref(&format->pool); +} + +void hash_format_deinit_free(struct hash_format **_format) +{ + struct hash_format *format = *_format; + + *_format = NULL; + pool_unref(&format->pool); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib/hash-format.h Tue Oct 19 18:10:34 2010 +0100 @@ -0,0 +1,19 @@ +#ifndef HASH_FORMAT_H +#define HASH_FORMAT_H + +struct hash_format; + +/* Initialize formatting hash. Format can contain text with %{sha1} style + variables. Each hash hash can be also truncated by specifying the number + of bits to truncate to, such as %{sha1:80}. */ +int hash_format_init(const char *format_string, struct hash_format **format_r, + const char **error_r); +/* Add more data to hash. */ +void hash_format_loop(struct hash_format *format, + const void *data, size_t size); +/* Write the hash into given string and free used memory. */ +void hash_format_deinit(struct hash_format **format, string_t *dest); +/* Free used memory without writing to string. */ +void hash_format_deinit_free(struct hash_format **format); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib/test-hash-format.c Tue Oct 19 18:10:34 2010 +0100 @@ -0,0 +1,54 @@ +/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str.h" +#include "hash-format.h" + +struct hash_format_test { + const char *input; + const char *output; +}; + +void test_hash_format(void) +{ + static const char *fail_input[] = { + "%", + "%A{sha1}", + "%{sha1", + "%{sha1:8", + "%{sha1:8a}", + "%{sha1:0}", + "%{sha1:168}", + NULL + }; + static struct hash_format_test tests[] = { + { "%{sha1}", "8843d7f92416211de9ebb963ff4ce28125932878" }, + { "*%{sha1}*", "*8843d7f92416211de9ebb963ff4ce28125932878*" }, + { "*%{sha1:8}*", "*88*" }, + { "%{sha1:152}", "8843d7f92416211de9ebb963ff4ce281259328" }, + { "%X{size}", "6" }, + { "%{sha256:80}", "c3ab8ff13720e8ad9047" }, + { "%{sha512:80}", "0a50261ebd1a390fed2b" }, + { "%{md4}", "547aefd231dcbaac398625718336f143" }, + { "%{md5}", "3858f62230ac3c915f300c664312c63f" }, + { "%{sha256:80}-%X{size}", "c3ab8ff13720e8ad9047-6" } + }; + struct hash_format *format; + string_t *str = t_str_new(128); + const char *error; + unsigned int i; + + test_begin("hash_format"); + for (i = 0; fail_input[i] != NULL; i++) + test_assert(hash_format_init(fail_input[i], &format, &error) < 0); + + for (i = 0; i < N_ELEMENTS(tests); i++) { + test_assert(hash_format_init(tests[i].input, &format, &error) == 0); + hash_format_loop(format, "foo", 3); + hash_format_loop(format, "bar", 3); + str_truncate(str, 0); + hash_format_deinit(&format, str); + test_assert(strcmp(str_c(str), tests[i].output) == 0); + } + test_end(); +}
--- a/src/lib/test-lib.c Tue Oct 19 18:09:16 2010 +0100 +++ b/src/lib/test-lib.c Tue Oct 19 18:10:34 2010 +0100 @@ -11,6 +11,7 @@ test_bsearch_insert_pos, test_buffer, test_crc32, + test_hash_format, test_hex_binary, test_istream_concat, test_istream_crlf,
--- a/src/lib/test-lib.h Tue Oct 19 18:09:16 2010 +0100 +++ b/src/lib/test-lib.h Tue Oct 19 18:10:34 2010 +0100 @@ -10,6 +10,7 @@ void test_bsearch_insert_pos(void); void test_buffer(void); void test_crc32(void); +void test_hash_format(void); void test_hex_binary(void); void test_istream_concat(void); void test_istream_crlf(void);