view src/plugins/listescape/listescape-plugin.c @ 12586:a2780b694b2d

lib-storage: mailbox_alloc() now takes a virtual mailbox name and other related API changes. All storage_name <-> vname conversions now go through the same two mailbox_list methods. This has many benefits, such as: * listescape plugin is now much simpler and bugfree * allows changing lib-storage API to use UTF-8 mailbox names in future * allows creation of "mailbox aliases" plugin
author Timo Sirainen <tss@iki.fi>
date Thu, 20 Jan 2011 20:59:07 +0200
parents 4073ccc8123f
children 447bce266022
line wrap: on
line source

/* Copyright (c) 2007-2010 Dovecot authors, see the included COPYING file */

#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_LIST_CONTEXT(obj) \
	MODULE_CONTEXT(obj, listescape_list_module)

struct listescape_mailbox_list {
	union mailbox_list_module_context module_ctx;
	char escape_char;
};

const char *listescape_plugin_version = DOVECOT_VERSION;

static MODULE_CONTEXT_DEFINE_INIT(listescape_list_module,
				  &mailbox_list_module_register);

static const char *
list_escape(struct listescape_mailbox_list *mlist,
	    struct mail_namespace *ns, const char *str)
{
	char ns_sep = mail_namespace_get_sep(ns);
	char list_sep = mailbox_list_get_hierarchy_sep(ns->list);
	string_t *esc = t_str_new(64);
	unsigned int i;

	for (i = 0; str[i] != '\0'; i++) {
		if (str[i] == '*' || str[i] == '%')
			break;
	}
	if (i > ns->prefix_len)
		i = ns->prefix_len;

	if (i > 0 && strncmp(ns->prefix, str, i) == 0) {
		str_append_n(esc, str, i);
		str += i;
	}

	if (*str == '~') {
		str_printfa(esc, "%c%02x", mlist->escape_char, *str);
		str++;
	}
	for (; *str != '\0'; str++) {
		if (*str == ns_sep)
			str_append_c(esc, *str);
		else if (*str == list_sep ||
			 *str == mlist->escape_char || *str == '/')
			str_printfa(esc, "%c%02x", mlist->escape_char, *str);
		else
			str_append_c(esc, *str);
	}
	return str_c(esc);
}

static const char *
list_unescape(struct listescape_mailbox_list *mlist,
	      struct mail_namespace *ns, const char *str)
{
	char ns_sep = mail_namespace_get_sep(ns);
	char list_sep = mailbox_list_get_hierarchy_sep(ns->list);
	string_t *dest = t_str_new(strlen(str));
	unsigned int num;

	for (; *str != '\0'; str++) {
		if (*str == mlist->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 == list_sep)
			str_append_c(dest, ns_sep);
		else
			str_append_c(dest, *str);
	}
	return str_c(dest);
}

static const char *listescape_list_get_vname(struct mailbox_list *list,
					     const char *storage_name)
{
	struct listescape_mailbox_list *mlist = LIST_ESCAPE_LIST_CONTEXT(list);
	const char *vname;

	vname = mlist->module_ctx.super.get_vname(list, storage_name);
	return list_unescape(mlist, list->ns, vname);
}

static const char *listescape_list_get_storage_name(struct mailbox_list *list,
						    const char *vname)
{
	struct listescape_mailbox_list *mlist = LIST_ESCAPE_LIST_CONTEXT(list);

	return mlist->module_ctx.super.
		get_storage_name(list, list_escape(mlist, list->ns, vname));
}

static void listescape_mailbox_list_created(struct mailbox_list *list)
{
	struct mailbox_list_vfuncs *v = list->vlast;
	struct listescape_mailbox_list *mlist;
	const char *env;
	char ns_sep = mail_namespace_get_sep(list->ns);
	char list_sep = mailbox_list_get_hierarchy_sep(list);

	if (list_sep == ns_sep)
		return;

	mlist = p_new(list->pool, struct listescape_mailbox_list, 1);
	mlist->module_ctx.super = *v;
	list->vlast = &mlist->module_ctx.super;
	v->get_vname = listescape_list_get_vname;
	v->get_storage_name = listescape_list_get_storage_name;

	env = mail_user_plugin_getenv(list->ns->user, "listescape_char");
	mlist->escape_char = env != NULL && *env != '\0' ?
		env[0] : DEFAULT_ESCAPE_CHAR;

	MODULE_CONTEXT_SET(list, listescape_list_module, mlist);
}

static struct mail_storage_hooks listescape_mail_storage_hooks = {
	.mailbox_list_created = listescape_mailbox_list_created
};

void listescape_plugin_init(struct module *module)
{
	mail_storage_hooks_add(module, &listescape_mail_storage_hooks);
}

void listescape_plugin_deinit(void)
{
	mail_storage_hooks_remove(&listescape_mail_storage_hooks);
}