changeset 8297:0d1bd98a6387 HEAD

Added listescape plugin.
author Timo Sirainen <tss@iki.fi>
date Sat, 18 Oct 2008 23:45:41 +0300
parents 698fca0d8b0a
children e88c6b43665a
files configure.in src/plugins/Makefile.am src/plugins/listescape/Makefile.am src/plugins/listescape/listescape-plugin.c src/plugins/listescape/listescape-plugin.h
diffstat 5 files changed, 363 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/configure.in	Sat Oct 18 22:51:20 2008 +0300
+++ b/configure.in	Sat Oct 18 23:45:41 2008 +0300
@@ -2448,6 +2448,7 @@
 src/plugins/fts-solr/Makefile
 src/plugins/fts-squat/Makefile
 src/plugins/lazy-expunge/Makefile
+src/plugins/listescape/Makefile
 src/plugins/mail-log/Makefile
 src/plugins/mbox-snarf/Makefile
 src/plugins/quota/Makefile
--- a/src/plugins/Makefile.am	Sat Oct 18 22:51:20 2008 +0300
+++ b/src/plugins/Makefile.am	Sat Oct 18 23:45:41 2008 +0300
@@ -18,6 +18,7 @@
 	fts \
 	fts-squat \
 	lazy-expunge \
+	listescape \
 	mail-log \
 	mbox-snarf \
 	quota \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/plugins/listescape/Makefile.am	Sat Oct 18 23:45:41 2008 +0300
@@ -0,0 +1,25 @@
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-mail \
+	-I$(top_srcdir)/src/lib-imap \
+	-I$(top_srcdir)/src/lib-index \
+	-I$(top_srcdir)/src/lib-storage
+
+lib20_listescape_plugin_la_LDFLAGS = -module -avoid-version
+
+module_LTLIBRARIES = \
+	lib20_listescape_plugin.la
+
+lib20_listescape_plugin_la_SOURCES = \
+	listescape-plugin.c
+
+noinst_HEADERS = \
+	listescape-plugin.h
+
+install-exec-local:
+	for d in imap pop3 lda; do \
+	  $(mkdir_p) $(DESTDIR)$(moduledir)/$$d; \
+	  rm -f $(DESTDIR)$(moduledir)/$$d/lib20_listescape_plugin$(MODULE_SUFFIX); \
+	  $(LN_S) ../lib20_listescape_plugin$(MODULE_SUFFIX) $(DESTDIR)$(moduledir)/$$d; \
+	done
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/plugins/listescape/listescape-plugin.c	Sat Oct 18 23:45:41 2008 +0300
@@ -0,0 +1,329 @@
+/* Copyright (C) 2007-2008 Timo Sirainen, LGPLv2.1 */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "module-context.h"
+#include "mail-storage-private.h"
+#include "mailbox-list-private.h"
+#include "listescape-plugin.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+
+#define DEFAULT_ESCAPE_CHAR '\\'
+
+#define LIST_ESCAPE_CONTEXT(obj) \
+	MODULE_CONTEXT(obj, listescape_storage_module)
+#define LIST_ESCAPE_LIST_CONTEXT(obj) \
+	MODULE_CONTEXT(obj, listescape_list_module)
+
+struct listescape_mail_storage {
+	union mail_storage_module_context module_ctx;
+};
+
+struct listescape_mailbox_list {
+	union mailbox_list_module_context module_ctx;
+	struct mailbox_info info;
+	string_t *list_name;
+	bool name_escaped;
+};
+
+const char *listescape_plugin_version = PACKAGE_VERSION;
+
+static void (*listescape_next_hook_mail_storage_created)
+	(struct mail_storage *storage);
+static void (*listescape_next_hook_mailbox_list_created)
+	(struct mailbox_list *list);
+static void (*listescape_next_hook_mail_namespaces_created)
+	(struct mail_namespace *namespaces);
+static char escape_char = DEFAULT_ESCAPE_CHAR;
+
+static MODULE_CONTEXT_DEFINE_INIT(listescape_storage_module,
+				  &mail_storage_module_register);
+static MODULE_CONTEXT_DEFINE_INIT(listescape_list_module,
+				  &mailbox_list_module_register);
+
+static const char *list_escape(struct mail_namespace *ns,
+			       const char *str, bool change_sep)
+{
+	string_t *esc = t_str_new(64);
+
+	if (*str == '~') {
+		str_printfa(esc, "%c%02x", escape_char, *str);
+		str++;
+	}
+	for (; *str != '\0'; str++) {
+		if (*str == ns->sep && change_sep)
+			str_append_c(esc, ns->list->hierarchy_sep);
+		else if (*str == ns->list->hierarchy_sep ||
+			 *str == escape_char || *str == '/')
+			str_printfa(esc, "%c%02x", escape_char, *str);
+		else
+			str_append_c(esc, *str);
+	}
+	return str_c(esc);
+}
+
+static void list_unescape_str(struct mail_namespace *ns,
+			      const char *str, string_t *dest)
+{
+	unsigned int num;
+
+	for (; *str != '\0'; str++) {
+		if (*str == escape_char &&
+		    i_isxdigit(str[1]) && i_isxdigit(str[2])) {
+			if (str[1] >= '0' && str[1] <= '9')
+				num = str[1] - '0';
+			else
+				num = i_toupper(str[1]) - 'A' + 10;
+			num *= 16;
+			if (str[2] >= '0' && str[2] <= '9')
+				num += str[2] - '0';
+			else
+				num += i_toupper(str[2]) - 'A' + 10;
+
+			str_append_c(dest, num);
+			str += 2;
+		} else if (*str == ns->list->hierarchy_sep)
+			str_append_c(dest, ns->sep);
+		else
+			str_append_c(dest, *str);
+	}
+}
+
+static struct mailbox_list_iterate_context *
+listescape_mailbox_list_iter_init(struct mailbox_list *list,
+				  const char *const *patterns,
+				  enum mailbox_list_iter_flags flags)
+{
+	struct listescape_mailbox_list *mlist = LIST_ESCAPE_LIST_CONTEXT(list);
+	struct mailbox_list_iterate_context *ctx;
+	const char **escaped_patterns;
+	unsigned int i;
+
+	t_push();
+	if ((flags & MAILBOX_LIST_ITER_VIRTUAL_NAMES) != 0) {
+		escaped_patterns = t_new(const char *,
+					 str_array_length(patterns) + 1);
+		for (i = 0; patterns[i] != NULL; i++) {
+			escaped_patterns[i] =
+				list_escape(list->ns, patterns[i], FALSE);
+		}
+		patterns = escaped_patterns;
+	}
+
+	/* Listing breaks if ns->real_sep isn't correct, but with everything
+	   else we need real_sep == virtual_sep. maybe some day lib-storage
+	   API gets changed so that it sees only virtual mailbox names and
+	   convers them internally and we don't have this problem. */
+	list->ns->real_sep = list->hierarchy_sep;
+	ctx = mlist->module_ctx.super.iter_init(list, patterns, flags);
+	list->ns->real_sep = list->ns->sep;
+	t_pop();
+	return ctx;
+}
+
+static const struct mailbox_info *
+listescape_mailbox_list_iter_next(struct mailbox_list_iterate_context *ctx)
+{
+	struct listescape_mailbox_list *mlist =
+		LIST_ESCAPE_LIST_CONTEXT(ctx->list);
+	const struct mailbox_info *info;
+
+	ctx->list->ns->real_sep = ctx->list->hierarchy_sep;
+	info = mlist->module_ctx.super.iter_next(ctx);
+	ctx->list->ns->real_sep = ctx->list->ns->sep;
+	if (info == NULL || (ctx->flags & MAILBOX_LIST_ITER_VIRTUAL_NAMES) == 0)
+		return info;
+
+	str_truncate(mlist->list_name, 0);
+	list_unescape_str(ctx->list->ns, info->name, mlist->list_name);
+	mlist->info = *info;
+	mlist->info.name = str_c(mlist->list_name);
+	return &mlist->info;
+}
+
+static int
+listescape_mailbox_list_iter_deinit(struct mailbox_list_iterate_context *ctx)
+{
+	struct mailbox_list *list = ctx->list;
+	struct listescape_mailbox_list *mlist =
+		LIST_ESCAPE_LIST_CONTEXT(ctx->list);
+	int ret;
+
+	list->ns->real_sep = list->hierarchy_sep;
+	ret = mlist->module_ctx.super.iter_deinit(ctx);
+	list->ns->real_sep = list->ns->sep;
+	return ret;
+}
+
+static struct mailbox *
+listescape_mailbox_open(struct mail_storage *storage, const char *name,
+			struct istream *input, enum mailbox_open_flags flags)
+{
+	struct listescape_mail_storage *mstorage =
+		LIST_ESCAPE_CONTEXT(storage);
+	struct listescape_mailbox_list *mlist =
+		LIST_ESCAPE_LIST_CONTEXT(storage->list);
+
+	if (!mlist->name_escaped)
+		name = list_escape(storage->ns, name, TRUE);
+	return mstorage->module_ctx.super.
+		mailbox_open(storage, name, input, flags);
+}
+
+static int
+listescape_mailbox_create(struct mail_storage *storage, const char *name,
+			  bool directory)
+{
+	struct listescape_mail_storage *mstorage =
+		LIST_ESCAPE_CONTEXT(storage);
+
+	name = list_escape(storage->ns, name, TRUE);
+	return mstorage->module_ctx.super.
+		mailbox_create(storage, name, directory);
+}
+
+static int
+listescape_delete_mailbox(struct mailbox_list *list, const char *name)
+{
+	struct listescape_mailbox_list *mlist = LIST_ESCAPE_LIST_CONTEXT(list);
+	int ret;
+
+	/* at least quota plugin opens the mailbox when deleting it */
+	name = list_escape(list->ns, name, TRUE);
+	mlist->name_escaped = TRUE;
+	ret = mlist->module_ctx.super.delete_mailbox(list, name);
+	mlist->name_escaped = FALSE;
+	return ret;
+}
+
+static int
+listescape_rename_mailbox(struct mailbox_list *list, const char *oldname,
+			  const char *newname)
+{
+	struct listescape_mailbox_list *mlist = LIST_ESCAPE_LIST_CONTEXT(list);
+
+	oldname = list_escape(list->ns, oldname, TRUE);
+	newname = list_escape(list->ns, newname, TRUE);
+	return mlist->module_ctx.super.rename_mailbox(list, oldname, newname);
+}
+
+static int listescape_set_subscribed(struct mailbox_list *list, 
+				     const char *name, bool set)
+{
+	struct listescape_mailbox_list *mlist = LIST_ESCAPE_LIST_CONTEXT(list);
+
+	name = list_escape(list->ns, name, TRUE);
+	return mlist->module_ctx.super.set_subscribed(list, name, set);
+}
+
+static int listescape_get_mailbox_name_status(struct mailbox_list *list,
+					      const char *name,
+					      enum mailbox_name_status *status)
+{
+	struct listescape_mailbox_list *mlist = LIST_ESCAPE_LIST_CONTEXT(list);
+
+	name = list_escape(list->ns, name, TRUE);
+	return mlist->module_ctx.super.
+		get_mailbox_name_status(list, name, status);
+}
+
+static bool listescape_is_valid_existing_name(struct mailbox_list *list,
+					      const char *name)
+{
+	struct listescape_mailbox_list *mlist = LIST_ESCAPE_LIST_CONTEXT(list);
+
+	name = list_escape(list->ns, name, TRUE);
+	return mlist->module_ctx.super.is_valid_existing_name(list, name);
+}
+
+static bool listescape_is_valid_create_name(struct mailbox_list *list,
+					    const char *name)
+{
+	struct listescape_mailbox_list *mlist = LIST_ESCAPE_LIST_CONTEXT(list);
+
+	name = list_escape(list->ns, name, TRUE);
+	return mlist->module_ctx.super.is_valid_create_name(list, name);
+}
+
+static void listescape_mail_storage_created(struct mail_storage *storage)
+{
+	struct listescape_mail_storage *mstorage;
+
+	if (listescape_next_hook_mail_storage_created != NULL)
+		listescape_next_hook_mail_storage_created(storage);
+
+	if (storage->list->hierarchy_sep == storage->ns->sep)
+		return;
+
+	mstorage = p_new(storage->pool, struct listescape_mail_storage, 1);
+	mstorage->module_ctx.super = storage->v;
+	storage->v.mailbox_open = listescape_mailbox_open;
+	storage->v.mailbox_create = listescape_mailbox_create;
+
+	MODULE_CONTEXT_SET(storage, listescape_storage_module, mstorage);
+}
+
+static void listescape_mailbox_list_created(struct mailbox_list *list)
+{
+	struct listescape_mailbox_list *mlist;
+
+	if (listescape_next_hook_mailbox_list_created != NULL)
+		listescape_next_hook_mailbox_list_created(list);
+
+	if (list->hierarchy_sep == list->ns->sep)
+		return;
+
+	mlist = p_new(list->pool, struct listescape_mailbox_list, 1);
+	mlist->module_ctx.super = list->v;
+	mlist->list_name = str_new(list->pool, 256);
+	list->v.iter_init = listescape_mailbox_list_iter_init;
+	list->v.iter_next = listescape_mailbox_list_iter_next;
+	list->v.iter_deinit = listescape_mailbox_list_iter_deinit;
+	list->v.delete_mailbox = listescape_delete_mailbox;
+	list->v.rename_mailbox = listescape_rename_mailbox;
+	list->v.set_subscribed = listescape_set_subscribed;
+	list->v.get_mailbox_name_status = listescape_get_mailbox_name_status;
+	list->v.is_valid_existing_name = listescape_is_valid_existing_name;
+	list->v.is_valid_create_name = listescape_is_valid_create_name;
+
+	MODULE_CONTEXT_SET(list, listescape_list_module, mlist);
+}
+
+static void
+listescape_mail_namespaces_created(struct mail_namespace *namespaces)
+{
+	for (; namespaces != NULL; namespaces = namespaces->next) {
+		if (namespaces->real_sep != namespaces->sep)
+			namespaces->real_sep = namespaces->sep;
+	}
+}
+
+void listescape_plugin_init(void)
+{
+	const char *env;
+
+	env = getenv("LISTESCAPE_CHAR");
+	if (env != NULL && *env != '\0')
+		escape_char = env[0];
+
+	listescape_next_hook_mail_storage_created = hook_mail_storage_created;
+	hook_mail_storage_created = listescape_mail_storage_created;
+
+	listescape_next_hook_mailbox_list_created = hook_mailbox_list_created;
+	hook_mailbox_list_created = listescape_mailbox_list_created;
+
+	listescape_next_hook_mail_namespaces_created =
+		hook_mail_namespaces_created;
+	hook_mail_namespaces_created = listescape_mail_namespaces_created;
+}
+
+void listescape_plugin_deinit(void)
+{
+	hook_mail_storage_created = listescape_next_hook_mail_storage_created;
+	hook_mailbox_list_created = listescape_next_hook_mailbox_list_created;
+	hook_mail_namespaces_created =
+		listescape_next_hook_mail_namespaces_created;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/plugins/listescape/listescape-plugin.h	Sat Oct 18 23:45:41 2008 +0300
@@ -0,0 +1,7 @@
+#ifndef LISTESCAPE_PLUGIN_H
+#define LISTESCAPE_PLUGIN_H
+
+void listescape_plugin_init(void);
+void listescape_plugin_deinit(void);
+
+#endif