]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
imapc: Added support for LIST/LSUB.
authorTimo Sirainen <tss@iki.fi>
Thu, 20 Jan 2011 20:45:41 +0000 (22:45 +0200)
committerTimo Sirainen <tss@iki.fi>
Thu, 20 Jan 2011 20:45:41 +0000 (22:45 +0200)
src/lib-storage/index/imapc/Makefile.am
src/lib-storage/index/imapc/imapc-list.c [new file with mode: 0644]
src/lib-storage/index/imapc/imapc-list.h [new file with mode: 0644]
src/lib-storage/index/imapc/imapc-storage.c
src/lib-storage/index/imapc/imapc-storage.h
src/lib-storage/register/Makefile.am

index 18d843f0853359ba4296456741640b563da39fe0..25bd450f7e68a969fef381e936dfa0b38c0ce2ff 100644 (file)
@@ -12,6 +12,7 @@ AM_CPPFLAGS = \
 libstorage_imapc_la_SOURCES = \
        imapc-client.c \
        imapc-connection.c \
+       imapc-list.c \
        imapc-mail.c \
        imapc-save.c \
        imapc-search.c \
@@ -21,6 +22,7 @@ libstorage_imapc_la_SOURCES = \
 
 headers = \
        imapc-connection.h \
+       imapc-list.h \
        imapc-seqmap.h \
        imapc-storage.h \
        imapc-sync.h
diff --git a/src/lib-storage/index/imapc/imapc-list.c b/src/lib-storage/index/imapc/imapc-list.c
new file mode 100644 (file)
index 0000000..1e3f85e
--- /dev/null
@@ -0,0 +1,357 @@
+/* Copyright (c) 2011 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "imap-arg.h"
+#include "imap-match.h"
+#include "mailbox-tree.h"
+#include "imapc-client.h"
+#include "imapc-storage.h"
+#include "imapc-list.h"
+
+struct imapc_mailbox_list_iterate_context {
+       struct mailbox_list_iterate_context ctx;
+       struct mailbox_tree_iterate_context *iter;
+       struct imap_match_glob *glob;
+       struct mailbox_info info;
+       bool failed;
+};
+
+extern struct mailbox_list imapc_mailbox_list;
+
+static struct mailbox_list *imapc_list_alloc(void)
+{
+       struct imapc_mailbox_list *list;
+       pool_t pool;
+
+       pool = pool_alloconly_create("imapc mailbox list", 1024);
+       list = p_new(pool, struct imapc_mailbox_list, 1);
+       list->list = imapc_mailbox_list;
+       list->list.pool = pool;
+       return &list->list;
+}
+
+static void imapc_list_deinit(struct mailbox_list *_list)
+{
+       struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list;
+
+       if (list->mailboxes != NULL)
+               mailbox_tree_deinit(&list->mailboxes);
+       if (list->subscriptions != NULL)
+               mailbox_tree_deinit(&list->subscriptions);
+       pool_unref(&list->list.pool);
+}
+
+static struct mailbox_node *
+imapc_list_update_tree(struct mailbox_tree_context *tree,
+                      const struct imap_arg *args)
+{
+       struct mailbox_node *node;
+       const struct imap_arg *flags;
+       const char *name, *flag;
+       enum mailbox_info_flags info_flags = 0;
+       bool created;
+
+       if (!imap_arg_get_list(&args[0], &flags) ||
+           args[1].type == IMAP_ARG_EOL ||
+           !imap_arg_get_astring(&args[2], &name))
+               return NULL;
+
+       while (imap_arg_get_atom(flags, &flag)) {
+               if (strcasecmp(flag, "\\NoSelect") == 0)
+                       info_flags |= MAILBOX_NOSELECT;
+               else if (strcasecmp(flag, "\\NonExistent") == 0)
+                       info_flags |= MAILBOX_NONEXISTENT;
+               else if (strcasecmp(flag, "\\NoInferiors") == 0)
+                       info_flags |= MAILBOX_NOINFERIORS;
+               else if (strcasecmp(flag, "\\Subscribed") == 0)
+                       info_flags |= MAILBOX_SUBSCRIBED;
+               flags++;
+       }
+
+       if ((info_flags & MAILBOX_NONEXISTENT) != 0)
+               node = mailbox_tree_lookup(tree, name);
+       else
+               node = mailbox_tree_get(tree, name, &created);
+       if (node != NULL)
+               node->flags = info_flags;
+       return node;
+}
+
+void imapc_list_update_mailbox(struct imapc_mailbox_list *list,
+                              const struct imap_arg *args)
+{
+       const char *sep, *name;
+
+       if (list->sep == '\0') {
+               /* we haven't asked for the separator yet.
+                  lets see if this is the reply for its request. */
+               if (args[0].type == IMAP_ARG_EOL ||
+                   !imap_arg_get_nstring(&args[1], &sep) ||
+                   !imap_arg_get_astring(&args[2], &name) || *name != '\0')
+                       return;
+
+               /* we can't handle NIL separator yet */
+               list->sep = sep == NULL ? '/' : sep[0];
+               return;
+       }
+       if (list->mailboxes == NULL)
+               list->mailboxes = mailbox_tree_init(list->sep);
+       (void)imapc_list_update_tree(list->mailboxes, args);
+}
+
+void imapc_list_update_subscription(struct imapc_mailbox_list *list,
+                                   const struct imap_arg *args)
+{
+       struct mailbox_node *node;
+
+       if (list->sep == '\0') {
+               /* we haven't asked for the separator yet */
+               return;
+       }
+       if (list->subscriptions == NULL)
+               list->subscriptions = mailbox_tree_init(list->sep);
+       node = imapc_list_update_tree(list->subscriptions, args);
+       if (node != NULL)
+               node->flags |= MAILBOX_SUBSCRIBED;
+}
+
+static int imapc_list_refresh(struct imapc_mailbox_list *list,
+                             enum mailbox_list_iter_flags flags)
+{
+       struct imapc_simple_context ctx;
+
+       ctx.storage = list->storage;
+       if ((flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0) {
+               imapc_client_cmdf(list->storage->client,
+                                 imapc_simple_callback, &ctx, "LIST \"\" *");
+               if (list->mailboxes != NULL)
+                       mailbox_tree_deinit(&list->mailboxes);
+       } else {
+               imapc_client_cmdf(list->storage->client,
+                                 imapc_simple_callback, &ctx, "LSUB \"\" *");
+               if (list->subscriptions != NULL)
+                       mailbox_tree_deinit(&list->subscriptions);
+       }
+
+       imapc_client_run(list->storage->client);
+       return ctx.ret;
+}
+
+static bool
+imapc_is_valid_pattern(struct mailbox_list *list, const char *pattern)
+{
+       return TRUE;
+}
+
+static bool
+imapc_is_valid_existing_name(struct mailbox_list *list, const char *name)
+{
+       return TRUE;
+}
+
+static bool
+imapc_is_valid_create_name(struct mailbox_list *list, const char *name)
+{
+       return TRUE;
+}
+
+static char imapc_list_get_hierarchy_sep(struct mailbox_list *_list)
+{
+       struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list;
+       struct imapc_simple_context ctx;
+
+       if (list->sep == '\0') {
+               ctx.storage = list->storage;
+               imapc_client_cmdf(list->storage->client,
+                                 imapc_simple_callback, &ctx,
+                                 "LIST \"\" \"\"");
+               imapc_client_run(list->storage->client);
+               if (ctx.ret < 0) {
+                       list->broken = TRUE;
+                       return '/';
+               }
+       }
+       return list->sep;
+}
+
+static const char *
+imapc_list_get_path(struct mailbox_list *list, const char *name,
+                   enum mailbox_list_path_type type)
+{
+       if (type == MAILBOX_LIST_PATH_TYPE_INDEX)
+               return "";
+       return NULL;
+}
+
+static const char *
+imapc_list_get_temp_prefix(struct mailbox_list *list, bool global ATTR_UNUSED)
+{
+       i_panic("imapc: Can't return a temp prefix for '%s'",
+               list->ns->prefix);
+       return NULL;
+}
+
+static const char *
+imapc_list_join_refpattern(struct mailbox_list *list ATTR_UNUSED,
+                          const char *ref, const char *pattern)
+{
+       return t_strconcat(ref, pattern, NULL);
+}
+
+static struct mailbox_list_iterate_context *
+imapc_list_iter_init(struct mailbox_list *_list, const char *const *patterns,
+                    enum mailbox_list_iter_flags flags)
+{
+       struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list;
+       struct imapc_mailbox_list_iterate_context *ctx;
+       struct mailbox_tree_context *tree;
+       char sep;
+
+       sep = mailbox_list_get_hierarchy_sep(_list);
+
+       ctx = i_new(struct imapc_mailbox_list_iterate_context, 1);
+       ctx->ctx.list = _list;
+       ctx->ctx.flags = flags;
+       ctx->info.ns = _list->ns;
+       ctx->glob = imap_match_init_multiple(default_pool, patterns,
+                                            FALSE, sep);
+       if (imapc_list_refresh(list, flags) < 0)
+               ctx->failed = TRUE;
+       else {
+               tree = (flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0 ?
+                       list->subscriptions : list->mailboxes;
+               ctx->iter = mailbox_tree_iterate_init(tree, NULL, 0);
+       }
+       return &ctx->ctx;
+}
+
+static const struct mailbox_info *
+imapc_list_iter_next(struct mailbox_list_iterate_context *_ctx)
+{
+       struct imapc_mailbox_list_iterate_context *ctx =
+               (struct imapc_mailbox_list_iterate_context *)_ctx;
+       struct mailbox_node *node;
+       const char *name;
+
+       if (ctx->failed)
+               return NULL;
+
+       while ((node = mailbox_tree_iterate_next(ctx->iter, &name)) != NULL) {
+               if (imap_match(ctx->glob, name) == IMAP_MATCH_YES) {
+                       ctx->info.flags &= ~(MAILBOX_CHILDREN |
+                                            MAILBOX_NOCHILDREN);
+                       if (node->children == NULL)
+                               ctx->info.flags |= MAILBOX_NOCHILDREN;
+                       else
+                               ctx->info.flags |= MAILBOX_CHILDREN;
+                       ctx->info.name = name;
+                       return &ctx->info;
+               }
+       }
+       return NULL;
+}
+
+static int imapc_list_iter_deinit(struct mailbox_list_iterate_context *_ctx)
+{
+       struct imapc_mailbox_list_iterate_context *ctx =
+               (struct imapc_mailbox_list_iterate_context *)_ctx;
+       int ret = ctx->failed ? -1 : 0;
+
+       if (ctx->iter != NULL)
+               mailbox_tree_iterate_deinit(&ctx->iter);
+       imap_match_deinit(&ctx->glob);
+       i_free(ctx);
+       return ret;
+}
+
+static int imapc_list_set_subscribed(struct mailbox_list *_list,
+                                    const char *name, bool set)
+{
+       struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list;
+       struct imapc_simple_context ctx;
+
+       ctx.storage = list->storage;
+       imapc_client_cmdf(list->storage->client,
+                         imapc_simple_callback, &ctx,
+                         set ? "SUBSCRIBE %s" : "UNSUBSCRIBE %s", name);
+       imapc_client_run(list->storage->client);
+       return ctx.ret;
+}
+
+static int
+imapc_list_create_mailbox_dir(struct mailbox_list *list, const char *name,
+                             enum mailbox_dir_create_type type)
+{
+       return -1;
+}
+
+static int
+imapc_list_delete_mailbox(struct mailbox_list *list, const char *name)
+{
+       return -1;
+}
+
+static int
+imapc_list_delete_dir(struct mailbox_list *list, const char *name)
+{
+       return -1;
+}
+
+static int
+imapc_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
+                         struct mailbox_list *newlist, const char *newname,
+                         bool rename_children)
+{
+       struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)oldlist;
+       struct imapc_simple_context ctx;
+
+       if (!rename_children) {
+               mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
+                       "Renaming without children not supported.");
+               return -1;
+       }
+
+       if (oldlist != newlist) {
+               mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
+                       "Can't rename mailboxes across storages.");
+               return -1;
+       }
+
+       ctx.storage = list->storage;
+       imapc_client_cmdf(list->storage->client,
+                         imapc_simple_callback, &ctx,
+                         "RENAME %s %s", oldname, newname);
+       imapc_client_run(list->storage->client);
+       return ctx.ret;
+}
+
+struct mailbox_list imapc_mailbox_list = {
+       .name = MAILBOX_LIST_NAME_IMAPC,
+       .props = 0,
+       .mailbox_name_max_length = MAILBOX_LIST_NAME_MAX_LENGTH,
+
+       {
+               imapc_list_alloc,
+               imapc_list_deinit,
+               NULL,
+               imapc_is_valid_pattern,
+               imapc_is_valid_existing_name,
+               imapc_is_valid_create_name,
+               imapc_list_get_hierarchy_sep,
+               mailbox_list_default_get_vname,
+               mailbox_list_default_get_storage_name,
+               imapc_list_get_path,
+               imapc_list_get_temp_prefix,
+               imapc_list_join_refpattern,
+               imapc_list_iter_init,
+               imapc_list_iter_next,
+               imapc_list_iter_deinit,
+               NULL,
+               NULL,
+               imapc_list_set_subscribed,
+               imapc_list_create_mailbox_dir,
+               imapc_list_delete_mailbox,
+               imapc_list_delete_dir,
+               imapc_list_rename_mailbox
+       }
+};
diff --git a/src/lib-storage/index/imapc/imapc-list.h b/src/lib-storage/index/imapc/imapc-list.h
new file mode 100644 (file)
index 0000000..371b95c
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef IMAPC_LIST_H
+#define IMAPC_LIST_H
+
+struct imap_arg;
+
+#include "mailbox-list-private.h"
+
+#define MAILBOX_LIST_NAME_IMAPC "imapc"
+
+struct imapc_mailbox_list {
+       struct mailbox_list list;
+       struct imapc_storage *storage;
+
+       struct mailbox_tree_context *mailboxes, *subscriptions;
+       char sep;
+
+       /* we've returned wrong separator. all mailbox list operations must
+          fail from now on. */
+       unsigned int broken:1;
+};
+
+void imapc_list_update_mailbox(struct imapc_mailbox_list *list,
+                              const struct imap_arg *args);
+void imapc_list_update_subscription(struct imapc_mailbox_list *list,
+                                   const struct imap_arg *args);
+
+#endif
index 54a144b2d9c5a183472cadfc67209b79f9ddf084..b0d7cfafeffa49cc190d7a37160fdcc67e4df4eb 100644 (file)
@@ -9,6 +9,7 @@
 #include "index-mail.h"
 #include "mailbox-list-private.h"
 #include "imapc-client.h"
+#include "imapc-list.h"
 #include "imapc-seqmap.h"
 #include "imapc-sync.h"
 #include "imapc-storage.h"
 
 #define DNS_CLIENT_SOCKET_NAME "dns-client"
 
-struct imapc_simple_context {
-       struct imapc_storage *storage;
-       int ret;
-};
-
 struct imapc_open_context {
        struct imapc_mailbox *mbox;
        int ret;
@@ -108,9 +104,8 @@ imapc_copy_error_from_reply(struct imapc_storage *storage,
        }
 }
 
-static void
-imapc_simple_callback(const struct imapc_command_reply *reply,
-                     void *context)
+void imapc_simple_callback(const struct imapc_command_reply *reply,
+                          void *context)
 {
        struct imapc_simple_context *ctx = context;
 
@@ -232,6 +227,11 @@ static void imapc_storage_untagged_cb(const struct imapc_untagged_reply *reply,
        struct imapc_seqmap *seqmap;
        uint32_t lseq;
 
+       if (strcasecmp(reply->name, "LIST") == 0)
+               imapc_list_update_mailbox(storage->list, reply->args);
+       else if (strcasecmp(reply->name, "LSUB") == 0)
+               imapc_list_update_subscription(storage->list, reply->args);
+
        if (mbox == NULL)
                return;
 
@@ -283,6 +283,8 @@ imapc_storage_create(struct mail_storage *_storage,
        set.dns_client_socket_path =
                t_strconcat(_storage->user->set->base_dir, "/",
                            DNS_CLIENT_SOCKET_NAME, NULL);
+       storage->list = (struct imapc_list *)ns->list;
+       storage->list->storage = storage;
        storage->client = imapc_client_init(&set);
        imapc_client_register_untagged(storage->client,
                                       imapc_storage_untagged_cb, storage);
@@ -300,7 +302,7 @@ static void
 imapc_storage_get_list_settings(const struct mail_namespace *ns ATTR_UNUSED,
                                struct mailbox_list_settings *set)
 {
-       set->layout = "none";
+       set->layout = MAILBOX_LIST_NAME_IMAPC;
 }
 
 static struct mailbox *
index b4447e1051d1b2fe1701d0894449d4a26eb58596..ad39d3473fd0e2a613120640b88a4ea219137f85 100644 (file)
@@ -10,6 +10,7 @@ struct imapc_command_reply;
 
 struct imapc_storage {
        struct mail_storage storage;
+       struct imapc_mailbox_list *list;
        struct imapc_client *client;
 };
 
@@ -26,6 +27,11 @@ struct imapc_mailbox {
        unsigned int new_msgs:1;
 };
 
+struct imapc_simple_context {
+       struct imapc_storage *storage;
+       int ret;
+};
+
 extern struct mail_vfuncs imapc_mail_vfuncs;
 
 struct mail_save_context *
@@ -48,6 +54,8 @@ bool imapc_search_next_nonblock(struct mail_search_context *_ctx,
                                struct mail *mail, bool *tryagain_r);
 void imapc_fetch_mail_update(struct mail *mail, const struct imap_arg *args);
 
+void imapc_simple_callback(const struct imapc_command_reply *reply,
+                          void *context);
 void imapc_async_stop_callback(const struct imapc_command_reply *reply,
                               void *context);
 
index 5ff9a522ef19d30c93c20dbff35aa863d48514ce..2dfb9d6d800899d7967af3f58b533aa117595950 100644 (file)
@@ -2,7 +2,7 @@ noinst_LTLIBRARIES = libstorage_register.la
 
 mail_storages = @mail_storages@
 
-mailbox_list_drivers = maildir imapdir none fs shared
+mailbox_list_drivers = maildir imapdir none fs shared imapc
 
 mail-storage-register.c: Makefile
        rm -f $@