]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Added a simple flat file dict backend.
authorTimo Sirainen <tss@iki.fi>
Sat, 18 Oct 2008 23:07:34 +0000 (02:07 +0300)
committerTimo Sirainen <tss@iki.fi>
Sat, 18 Oct 2008 23:07:34 +0000 (02:07 +0300)
--HG--
branch : HEAD

src/deliver/deliver.c
src/imap/main.c
src/lib-dict/Makefile.am
src/lib-dict/dict-client.h
src/lib-dict/dict-file.c [new file with mode: 0644]
src/lib-dict/dict-private.h
src/lib-dict/dict.c
src/lib-dict/dict.h
src/plugins/expire/expire-tool.c
src/pop3/main.c

index 0b361bd331ddeb76f6c63fdc05678c047f4c9594..201ed9acce847288589ae4c7eaab9bc76c8f81c3 100644 (file)
@@ -24,7 +24,7 @@
 #include "message-address.h"
 #include "mail-namespace.h"
 #include "raw-storage.h"
-#include "dict-client.h"
+#include "dict.h"
 #include "auth-client.h"
 #include "mail-send.h"
 #include "duplicate.h"
@@ -1019,7 +1019,7 @@ int main(int argc, char *argv[])
        if (deliver_set->log_format == NULL)
                deliver_set->log_format = DEFAULT_LOG_FORMAT;
 
-       dict_driver_register(&dict_driver_client);
+       dict_drivers_register_builtin();
         duplicate_init();
         mail_storage_init();
        mail_storage_register_all();
@@ -1147,7 +1147,7 @@ int main(int argc, char *argv[])
        mail_storage_deinit();
 
        duplicate_deinit();
-       dict_driver_unregister(&dict_driver_client);
+       dict_drivers_unregister_builtin();
        lib_signals_deinit();
 
        io_loop_destroy(&ioloop);
index eb4156784d3bdf753df6b9b0545f9234a4d9e735..0bd6fdda39549c49ecc923ecfd5358c8641f97c1 100644 (file)
@@ -12,7 +12,7 @@
 #include "fd-close-on-exec.h"
 #include "process-title.h"
 #include "module-dir.h"
-#include "dict-client.h"
+#include "dict.h"
 #include "mail-storage.h"
 #include "commands.h"
 #include "mail-namespace.h"
@@ -193,7 +193,7 @@ static void main_init(void)
        capability_string = str_new(default_pool, sizeof(CAPABILITY_STRING)+32);
        str_append(capability_string, CAPABILITY_STRING);
 
-       dict_driver_register(&dict_driver_client);
+       dict_drivers_register_builtin();
         mail_storage_init();
        mail_storage_register_all();
        mailbox_list_register_all();
@@ -272,7 +272,7 @@ static void main_deinit(void)
        module_dir_unload(&modules);
        commands_deinit();
         mail_storage_deinit();
-       dict_driver_unregister(&dict_driver_client);
+       dict_drivers_unregister_builtin();
 
        str_free(&capability_string);
 
index 1b2dc8aaf14599369d5a3983836805fdc038d11d..eaa5c4e54654667a82b9a7208616128d79dc7e8f 100644 (file)
@@ -11,7 +11,8 @@ AM_CPPFLAGS = \
 
 base_sources = \
        dict.c \
-       dict-client.c
+       dict-client.c \
+       dict-file.c
 
 backend_sources = \
        dict-db.c \
index 4c1acee04bb5756a2a1e7e5b714bd01112a1e97d..94064f85a4a354ea889dfac744ebe523e801a8a9 100644 (file)
@@ -33,8 +33,6 @@ enum {
        DICT_PROTOCOL_REPLY_FAIL = 'F'
 };
 
-extern struct dict dict_driver_client;
-
 const char *dict_client_escape(const char *src);
 const char *dict_client_unescape(const char *src);
 
diff --git a/src/lib-dict/dict-file.c b/src/lib-dict/dict-file.c
new file mode 100644 (file)
index 0000000..453ee8b
--- /dev/null
@@ -0,0 +1,401 @@
+/* Copyright (c) 2008 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "hash.h"
+#include "file-dotlock.h"
+#include "nfs-workarounds.h"
+#include "istream.h"
+#include "ostream.h"
+#include "dict-private.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+struct file_dict {
+       struct dict dict;
+       pool_t hash_pool;
+
+       char *path;
+       struct hash_table *hash;
+       int fd;
+};
+
+struct file_dict_iterate_context {
+       struct dict_iterate_context ctx;
+
+       struct hash_iterate_context *iter;
+       char *path;
+       unsigned int path_len;
+
+       enum dict_iterate_flags flags;
+       unsigned int failed:1;
+};
+
+enum file_dict_change_type {
+       FILE_DICT_CHANGE_TYPE_SET,
+       FILE_DICT_CHANGE_TYPE_UNSET,
+       FILE_DICT_CHANGE_TYPE_INC
+};
+
+struct file_dict_change {
+       enum file_dict_change_type type;
+       const char *key;
+       union {
+               const char *str;
+               long long diff;
+       } value;
+};
+
+struct file_dict_transaction_context {
+       struct dict_transaction_context ctx;
+
+       pool_t pool;
+       ARRAY_DEFINE(changes, struct file_dict_change);
+};
+
+static struct dotlock_settings file_dict_dotlock_settings = {
+       MEMBER(temp_prefix) NULL,
+       MEMBER(lock_suffix) NULL,
+
+       MEMBER(timeout) 30,
+       MEMBER(stale_timeout) 5
+};
+
+static struct dict *file_dict_init(struct dict *driver, const char *uri,
+                                  enum dict_data_type value_type ATTR_UNUSED,
+                                  const char *username ATTR_UNUSED)
+{
+       struct file_dict *dict;
+       
+       dict = i_new(struct file_dict, 1);
+       dict->dict = *driver;
+       dict->path = i_strdup(uri);
+       dict->hash_pool = pool_alloconly_create("file dict", 1024);
+       dict->hash = hash_create(default_pool, dict->hash_pool, 0, str_hash,
+                                (hash_cmp_callback_t *)strcmp);
+       dict->fd = -1;
+       return &dict->dict;
+}
+
+static void file_dict_deinit(struct dict *_dict)
+{
+       struct file_dict *dict = (struct file_dict *)_dict;
+
+       hash_destroy(&dict->hash);
+       pool_unref(&dict->hash_pool);
+       i_free(dict->path);
+       i_free(dict);
+}
+
+static bool file_dict_need_refresh(struct file_dict *dict)
+{
+       struct stat st1, st2;
+
+       if (dict->fd == -1)
+               return TRUE;
+
+       nfs_flush_file_handle_cache(dict->path);
+       if (nfs_safe_stat(dict->path, &st1) < 0) {
+               i_error("stat(%s) failed: %m", dict->path);
+               return FALSE;
+       }
+
+       if (fstat(dict->fd, &st2) < 0) {
+               if (errno != ESTALE)
+                       i_error("fstat(%s) failed: %m", dict->path);
+               return TRUE;
+       }
+       if (st1.st_ino != st2.st_ino ||
+           !CMP_DEV_T(st1.st_dev, st2.st_dev)) {
+               /* file changed */
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static int file_dict_refresh(struct file_dict *dict)
+{
+       struct istream *input;
+       char *key, *value;
+
+       if (!file_dict_need_refresh(dict))
+               return 0;
+
+       if (dict->fd != -1) {
+               if (close(dict->fd) < 0)
+                       i_error("close(%s) failed: %m", dict->path);
+       }
+       dict->fd = open(dict->path, O_RDONLY);
+       if (dict->fd == -1) {
+               if (errno == ENOENT)
+                       return 0;
+               i_error("open(%s) failed: %m", dict->path);
+               return -1;
+       }
+
+       hash_clear(dict->hash, TRUE);
+       p_clear(dict->hash_pool);
+
+       input = i_stream_create_fd(dict->fd, (size_t)-1, FALSE);
+       while ((key = i_stream_read_next_line(input)) != NULL &&
+              (value = i_stream_read_next_line(input)) != NULL) {
+               key = p_strdup(dict->hash_pool, key);
+               value = p_strdup(dict->hash_pool, value);
+               hash_insert(dict->hash, key, value);
+       }
+       i_stream_destroy(&input);
+       return 0;
+}
+
+static int file_dict_lookup(struct dict *_dict, pool_t pool,
+                           const char *key, const char **value_r)
+{
+       struct file_dict *dict = (struct file_dict *)_dict;
+
+       if (file_dict_refresh(dict) < 0)
+               return -1;
+
+       *value_r = p_strdup(pool, hash_lookup(dict->hash, key));
+       return *value_r == NULL ? 0 : 1;
+}
+
+static struct dict_iterate_context *
+file_dict_iterate_init(struct dict *_dict, const char *path,
+                      enum dict_iterate_flags flags)
+{
+        struct file_dict_iterate_context *ctx;
+       struct file_dict *dict = (struct file_dict *)_dict;
+
+       ctx = i_new(struct file_dict_iterate_context, 1);
+       ctx->path = i_strdup(path);
+       ctx->path_len = strlen(path);
+       ctx->flags = flags;
+       ctx->iter = hash_iterate_init(dict->hash);
+
+       if (file_dict_refresh(dict) < 0)
+               ctx->failed = TRUE;
+       return &ctx->ctx;
+}
+
+static int file_dict_iterate(struct dict_iterate_context *_ctx,
+                            const char **key_r, const char **value_r)
+{
+       struct file_dict_iterate_context *ctx =
+               (struct file_dict_iterate_context *)_ctx;
+       void *key, *value;
+
+       while (hash_iterate(ctx->iter, &key, &value)) {
+               if (strncmp(ctx->path, key, ctx->path_len) != 0)
+                       continue;
+
+               if ((ctx->flags & DICT_ITERATE_FLAG_RECURSE) == 0 &&
+                   strchr(key + ctx->path_len, '/') != NULL)
+                       continue;
+
+               *key_r = key;
+               *value_r = value;
+               return 1;
+       }
+       return ctx->failed ? -1 : 0;
+}
+
+static void file_dict_iterate_deinit(struct dict_iterate_context *_ctx)
+{
+       struct file_dict_iterate_context *ctx =
+               (struct file_dict_iterate_context *)_ctx;
+
+       hash_iterate_deinit(&ctx->iter);
+       i_free(ctx->path);
+       i_free(ctx);
+}
+
+static struct dict_transaction_context *
+file_dict_transaction_init(struct dict *_dict)
+{
+       struct file_dict_transaction_context *ctx;
+       pool_t pool;
+
+       pool = pool_alloconly_create("file dict transaction", 1024);
+       ctx = p_new(pool, struct file_dict_transaction_context, 1);
+       ctx->ctx.dict = _dict;
+       ctx->pool = pool;
+       p_array_init(&ctx->changes, pool, 32);
+       return &ctx->ctx;
+}
+
+static void file_dict_apply_changes(struct file_dict_transaction_context *ctx)
+{
+       struct file_dict *dict = (struct file_dict *)ctx->ctx.dict;
+       const char *tmp;
+       char *key, *value, *old_value;
+       void *orig_key, *orig_value;
+       const struct file_dict_change *changes;
+       unsigned int i, count, new_len;
+       long long diff;
+
+       changes = array_get(&ctx->changes, &count);
+       for (i = 0; i < count; i++) {
+               if (hash_lookup_full(dict->hash, changes[i].key,
+                                    &orig_key, &orig_value)) {
+                       key = orig_key;
+                       old_value = orig_value;
+               } else {
+                       key = NULL;
+                       old_value = NULL;
+               }
+               value = NULL;
+
+               switch (changes[i].type) {
+               case FILE_DICT_CHANGE_TYPE_INC:
+                       diff = old_value == NULL ? 0 :
+                               strtoll(old_value, NULL, 10);
+                       diff += changes[i].value.diff;
+                       tmp = t_strdup_printf("%lld", diff);
+                       new_len = strlen(tmp);
+                       if (new_len > strlen(old_value))
+                               value = p_strdup(dict->hash_pool, tmp);
+                       else {
+                               memcpy(old_value, tmp, new_len + 1);
+                               value = old_value;
+                       }
+                       /* fall through */
+               case FILE_DICT_CHANGE_TYPE_SET:
+                       if (key == NULL)
+                               key = p_strdup(dict->hash_pool, changes[i].key);
+                       if (value == NULL) {
+                               value = p_strdup(dict->hash_pool,
+                                                changes[i].value.str);
+                       }
+                       hash_update(dict->hash, key, value);
+                       break;
+               case FILE_DICT_CHANGE_TYPE_UNSET:
+                       if (old_value != NULL)
+                               hash_remove(dict->hash, key);
+                       break;
+               }
+       }
+}
+
+static int file_dict_write_changes(struct file_dict_transaction_context *ctx)
+{
+       struct file_dict *dict = (struct file_dict *)ctx->ctx.dict;
+       struct dotlock *dotlock;
+       struct hash_iterate_context *iter;
+       struct ostream *output;
+       void *key, *value;
+       int fd;
+
+       fd = file_dotlock_open(&file_dict_dotlock_settings, dict->path, 0,
+                              &dotlock);
+       if (fd == -1) {
+               i_error("file dict commit: file_dotlock_open() failed: %m");
+               return -1;
+       }
+       /* refresh once more now that we're locked */
+       if (file_dict_refresh(dict) < 0) {
+               file_dotlock_delete(&dotlock);
+               return -1;
+       }
+       file_dict_apply_changes(ctx);
+
+       output = o_stream_create_fd(fd, 0, FALSE);
+       iter = hash_iterate_init(dict->hash);
+       while (hash_iterate(iter, &key, &value)) {
+               o_stream_send_str(output, key);
+               o_stream_send(output, "\n", 1);
+               o_stream_send_str(output, value);
+               o_stream_send(output, "\n", 1);
+       }
+       hash_iterate_deinit(&iter);
+       o_stream_destroy(&output);
+
+       if (file_dotlock_replace(&dotlock,
+                                DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) < 0) {
+               (void)close(fd);
+               return -1;
+       }
+
+       if (dict->fd != -1)
+               (void)close(dict->fd);
+       dict->fd = fd;
+       return 0;
+}
+
+static int file_dict_transaction_commit(struct dict_transaction_context *_ctx)
+{
+       struct file_dict_transaction_context *ctx =
+               (struct file_dict_transaction_context *)_ctx;
+       int ret;
+
+       ret = file_dict_write_changes(ctx);
+       pool_unref(&ctx->pool);
+       return ret;
+}
+
+static void file_dict_transaction_rollback(struct dict_transaction_context *_ctx)
+{
+       struct file_dict_transaction_context *ctx =
+               (struct file_dict_transaction_context *)_ctx;
+
+       pool_unref(&ctx->pool);
+}
+
+static void file_dict_set(struct dict_transaction_context *_ctx,
+                         const char *key, const char *value)
+{
+       struct file_dict_transaction_context *ctx =
+               (struct file_dict_transaction_context *)_ctx;
+       struct file_dict_change *change;
+
+       change = array_append_space(&ctx->changes);
+       change->type = FILE_DICT_CHANGE_TYPE_SET;
+       change->key = p_strdup(ctx->pool, key);
+       change->value.str = p_strdup(ctx->pool, value);
+}
+
+static void file_dict_unset(struct dict_transaction_context *_ctx,
+                           const char *key)
+{
+       struct file_dict_transaction_context *ctx =
+               (struct file_dict_transaction_context *)_ctx;
+       struct file_dict_change *change;
+
+       change = array_append_space(&ctx->changes);
+       change->type = FILE_DICT_CHANGE_TYPE_UNSET;
+       change->key = p_strdup(ctx->pool, key);
+}
+
+static void
+file_dict_atomic_inc(struct dict_transaction_context *_ctx,
+                    const char *key, long long diff)
+{
+       struct file_dict_transaction_context *ctx =
+               (struct file_dict_transaction_context *)_ctx;
+       struct file_dict_change *change;
+
+       change = array_append_space(&ctx->changes);
+       change->type = FILE_DICT_CHANGE_TYPE_INC;
+       change->key = p_strdup(ctx->pool, key);
+       change->value.diff = diff;
+}
+
+struct dict dict_driver_file = {
+       MEMBER(name) "file",
+       {
+               file_dict_init,
+               file_dict_deinit,
+               file_dict_lookup,
+               file_dict_iterate_init,
+               file_dict_iterate,
+               file_dict_iterate_deinit,
+               file_dict_transaction_init,
+               file_dict_transaction_commit,
+               file_dict_transaction_rollback,
+               file_dict_set,
+               file_dict_unset,
+               file_dict_atomic_inc
+       }
+};
index e49951f7884d1079e68ffcaa1b15e7ed8e5aa302..79e29f62440305c4cb62071e810720441c21870c 100644 (file)
@@ -47,4 +47,7 @@ struct dict_transaction_context {
        unsigned int changed:1;
 };
 
+extern struct dict dict_driver_file;
+extern struct dict dict_driver_client;
+
 #endif
index 23cfb889edb1fcda31379655c1ab3791433b9e57..3edd40448398441a0b86fb8b58054ee91343a392 100644 (file)
@@ -51,6 +51,18 @@ void dict_driver_unregister(struct dict *driver)
                array_free(&dict_drivers);
 }
 
+void dict_drivers_register_builtin(void)
+{
+       dict_driver_register(&dict_driver_client);
+       dict_driver_register(&dict_driver_file);
+}
+
+void dict_drivers_unregister_builtin(void)
+{
+       dict_driver_unregister(&dict_driver_client);
+       dict_driver_unregister(&dict_driver_file);
+}
+
 struct dict *dict_init(const char *uri, enum dict_data_type value_type,
                       const char *username)
 {
index e5211ef79777fdd2510b285282fec0a8421ab505..34c9019e5b7f5726896e0a687dd507875c4e47f0 100644 (file)
@@ -20,6 +20,9 @@ enum dict_data_type {
 void dict_driver_register(struct dict *driver);
 void dict_driver_unregister(struct dict *driver);
 
+void dict_drivers_register_builtin(void);
+void dict_drivers_unregister_builtin(void);
+
 void dict_drivers_register_all(void);
 void dict_drivers_unregister_all(void);
 
index 994ddcb53c03d1fe12e35683e44775a4d1608518..708683987e523bf6bdc03989a4200ff79612e5fb 100644 (file)
@@ -6,7 +6,7 @@
 #include "file-lock.h"
 #include "randgen.h"
 #include "lib-signals.h"
-#include "dict-client.h"
+#include "dict.h"
 #include "mail-index.h"
 #include "mail-search-build.h"
 #include "mail-storage.h"
@@ -188,7 +188,7 @@ static void expire_run(bool testrun)
        const char *userp, *mailbox;
        int ret;
 
-       dict_driver_register(&dict_driver_client);
+       dict_drivers_register_builtin();
        mail_storage_init();
        mail_storage_register_all();
        mailbox_list_register_all();
@@ -295,7 +295,7 @@ static void expire_run(bool testrun)
        auth_connection_deinit(ctx.auth_conn);
 
        mail_storage_deinit();
-       dict_driver_unregister(&dict_driver_client);
+       dict_drivers_unregister_builtin();
 }
 
 int main(int argc ATTR_UNUSED, const char *argv[])
index ff9398762a5fcafa9cf5f28aa8041ad38076e5b6..449194a12d340e8a62f8a1f796bbc50af9ecf96c 100644 (file)
@@ -13,7 +13,7 @@
 #include "process-title.h"
 #include "module-dir.h"
 #include "var-expand.h"
-#include "dict-client.h"
+#include "dict.h"
 #include "mail-storage.h"
 #include "mail-namespace.h"
 
@@ -204,7 +204,7 @@ static bool main_init(void)
                                log_error_callback, NULL);
        }
 
-       dict_driver_register(&dict_driver_client);
+       dict_drivers_register_builtin();
         mail_storage_init();
        mail_storage_register_all();
        mailbox_list_register_all();
@@ -261,7 +261,7 @@ static void main_deinit(void)
 
        module_dir_unload(&modules);
        mail_storage_deinit();
-       dict_driver_unregister(&dict_driver_client);
+       dict_drivers_unregister_builtin();
 
        lib_signals_deinit();
        closelog();