diff options
Diffstat (limited to 'src/lib-dict-backend/dict-cdb.c')
-rw-r--r-- | src/lib-dict-backend/dict-cdb.c | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/src/lib-dict-backend/dict-cdb.c b/src/lib-dict-backend/dict-cdb.c new file mode 100644 index 0000000..c6fad30 --- /dev/null +++ b/src/lib-dict-backend/dict-cdb.c @@ -0,0 +1,266 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" + +#ifdef BUILD_CDB +#include "dict-private.h" + +#include <string.h> +#include <cdb.h> +#include <unistd.h> +#include <fcntl.h> + +#define CDB_WITH_NULL 1 +#define CDB_WITHOUT_NULL 2 + +struct cdb_dict { + struct dict dict; + struct cdb cdb; + char *path; + int fd, flag; +}; + +struct cdb_dict_iterate_context { + struct dict_iterate_context ctx; + + enum dict_iterate_flags flags; + buffer_t *buffer; + const char *values[2]; + char *path; + unsigned cptr; + char *error; +}; + +static void cdb_dict_deinit(struct dict *_dict); + +static int +cdb_dict_init(struct dict *driver, const char *uri, + const struct dict_settings *set ATTR_UNUSED, + struct dict **dict_r, const char **error_r) +{ + struct cdb_dict *dict; + + dict = i_new(struct cdb_dict, 1); + dict->dict = *driver; + dict->path = i_strdup(uri); + dict->flag = CDB_WITH_NULL | CDB_WITHOUT_NULL; + + /* initialize cdb to 0 (unallocated) */ + i_zero(&dict->cdb); + + dict->fd = open(dict->path, O_RDONLY); + if (dict->fd == -1) { + *error_r = t_strdup_printf("open(%s) failed: %m", dict->path); + cdb_dict_deinit(&dict->dict); + return -1; + } + +#ifdef TINYCDB_VERSION + if (cdb_init(&dict->cdb, dict->fd) < 0) { + *error_r = t_strdup_printf("cdb_init(%s) failed: %m", dict->path); + cdb_dict_deinit(&dict->dict); + return -1; + } +#else + cdb_init(&dict->cdb, dict->fd); +#endif + + *dict_r = &dict->dict; + return 0; +} + +static void cdb_dict_deinit(struct dict *_dict) +{ + struct cdb_dict *dict = (struct cdb_dict *)_dict; + + /* we can safely deinit unallocated cdb */ + cdb_free(&dict->cdb); + + i_close_fd_path(&dict->fd, dict->path); + + i_free(dict->path); + i_free(dict); +} + +static int +cdb_dict_lookup(struct dict *_dict, + const struct dict_op_settings *set ATTR_UNUSED, + pool_t pool, + const char *key, const char **value_r, + const char **error_r) +{ + struct cdb_dict *dict = (struct cdb_dict *)_dict; + unsigned datalen; + int ret = 0; + char *data; + + /* keys and values may be null terminated... */ + if ((dict->flag & CDB_WITH_NULL) != 0) { + ret = cdb_find(&dict->cdb, key, (unsigned)strlen(key)+1); + if (ret > 0) + dict->flag &= ENUM_NEGATE(CDB_WITHOUT_NULL); + } + + /* ...or not */ + if (ret == 0 && (dict->flag & CDB_WITHOUT_NULL) != 0) { + ret = cdb_find(&dict->cdb, key, (unsigned)strlen(key)); + if (ret > 0) + dict->flag &= ENUM_NEGATE(CDB_WITH_NULL); + } + + if (ret <= 0) { + *value_r = NULL; + /* something bad with db */ + if (ret < 0) { + *error_r = t_strdup_printf("cdb_find(%s) failed: %m", dict->path); + return -1; + } + /* found nothing */ + return 0; + } + + datalen = cdb_datalen(&dict->cdb); + data = p_malloc(pool, datalen + 1); + if (cdb_read(&dict->cdb, data, datalen, cdb_datapos(&dict->cdb)) < 0) { + *error_r = t_strdup_printf("cdb_read(%s) failed: %m", dict->path); + return -1; + } + *value_r = data; + return 1; +} + +static struct dict_iterate_context * +cdb_dict_iterate_init(struct dict *_dict, + const struct dict_op_settings *set ATTR_UNUSED, + const char *path, enum dict_iterate_flags flags) +{ + struct cdb_dict_iterate_context *ctx = + i_new(struct cdb_dict_iterate_context, 1); + struct cdb_dict *dict = (struct cdb_dict *)_dict; + + ctx->ctx.dict = &dict->dict; + ctx->path = i_strdup(path); + ctx->flags = flags; + ctx->buffer = buffer_create_dynamic(default_pool, 256); + + cdb_seqinit(&ctx->cptr, &dict->cdb); + + return &ctx->ctx; +} + +static bool +cdb_dict_next(struct cdb_dict_iterate_context *ctx, const char **key_r) +{ + struct cdb_dict *dict = (struct cdb_dict *)ctx->ctx.dict; + char *data; + unsigned datalen; + int ret; + + if ((ret = cdb_seqnext(&ctx->cptr, &dict->cdb)) < 1) { + if (ret < 0) + ctx->error = i_strdup_printf("cdb_seqnext(%s) failed: %m", + dict->path); + return FALSE; + } + + buffer_set_used_size(ctx->buffer, 0); + + datalen = cdb_keylen(&dict->cdb); + data = buffer_append_space_unsafe(ctx->buffer, datalen + 1); + + if (cdb_read(&dict->cdb, data, datalen, cdb_keypos(&dict->cdb)) < 0) { + ctx->error = i_strdup_printf("cdb_read(%s) failed: %m", + dict->path); + return FALSE; + } + + data[datalen] = '\0'; + *key_r = data; + + return TRUE; +} + +static bool cdb_dict_iterate(struct dict_iterate_context *_ctx, + const char **key_r, const char *const **values_r) +{ + struct cdb_dict_iterate_context *ctx = + (struct cdb_dict_iterate_context *)_ctx; + struct cdb_dict *dict = (struct cdb_dict *)_ctx->dict; + const char *key; + bool match = FALSE; + char *data; + unsigned datalen; + + if (ctx->error != NULL) + return FALSE; + + while(!match && cdb_dict_next(ctx, &key)) { + if (((ctx->flags & DICT_ITERATE_FLAG_EXACT_KEY) != 0 && + strcmp(key, ctx->path) == 0) || + ((ctx->flags & DICT_ITERATE_FLAG_RECURSE) != 0 && + str_begins(key, ctx->path)) || + ((ctx->flags & DICT_ITERATE_FLAG_RECURSE) == 0 && + str_begins(key, ctx->path) && + strchr(key + strlen(ctx->path), '/') == NULL)) { + match = TRUE; + break; + } + } + + if (!match) + return FALSE; + + *key_r = key; + + if ((ctx->flags & DICT_ITERATE_FLAG_NO_VALUE) != 0) + return TRUE; + + datalen = cdb_datalen(&dict->cdb); + data = buffer_append_space_unsafe(ctx->buffer, datalen + 1); + + if (cdb_read(&dict->cdb, data, datalen, cdb_datapos(&dict->cdb)) < 0) { + ctx->error = i_strdup_printf("cdb_read(%s) failed: %m", + dict->path); + return FALSE; + } + + data[datalen] = '\0'; + ctx->values[0] = data; + *values_r = ctx->values; + + return TRUE; +} + +static int cdb_dict_iterate_deinit(struct dict_iterate_context *_ctx, + const char **error_r) +{ + int ret = 0; + struct cdb_dict_iterate_context *ctx = + (struct cdb_dict_iterate_context *)_ctx; + if (ctx->error != NULL) { + *error_r = t_strdup(ctx->error); + ret = -1; + } + + buffer_free(&ctx->buffer); + i_free(ctx->error); + i_free(ctx->path); + i_free(ctx); + + return ret; +} + + +struct dict dict_driver_cdb = { + .name = "cdb", + { + .init = cdb_dict_init, + .deinit = cdb_dict_deinit, + .lookup = cdb_dict_lookup, + .iterate_init = cdb_dict_iterate_init, + .iterate = cdb_dict_iterate, + .iterate_deinit = cdb_dict_iterate_deinit, + } +}; +#endif |