--- dovecot-1.0.15.orig/dovecot-sieve/autogen.sh
+++ dovecot-1.0.15/dovecot-sieve/autogen.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+# If you've non-standard directories, set these
+#ACLOCAL_DIR=
+#GETTEXT_DIR=
+
+if test "$ACLOCAL_DIR" != ""; then
+  ACLOCAL="aclocal -I $ACLOCAL_DIR"
+  export ACLOCAL
+fi
+
+if test ! -f ChangeLog; then
+  # automake dies unless this exists. It's generated in Makefile
+  touch -t `date +%m%d`0000 ChangeLog
+fi
+
+autoreconf -i
--- dovecot-1.0.15.orig/dovecot-sieve/stamp.h.in
+++ dovecot-1.0.15/dovecot-sieve/stamp.h.in
@@ -0,0 +1 @@
+ 
--- dovecot-1.0.15.orig/dovecot-sieve/README
+++ dovecot-1.0.15/dovecot-sieve/README
@@ -0,0 +1,3 @@
+See INSTALL on how to install this plugin.
+
+The libsieve is based on Cyrus v2.2's libsieve.
--- dovecot-1.0.15.orig/dovecot-sieve/configure.in
+++ dovecot-1.0.15/dovecot-sieve/configure.in
@@ -0,0 +1,68 @@
+AC_INIT(dovecot-sieve, 1.0.2, [dovecot@dovecot.org])
+AC_CONFIG_SRCDIR([src])
+
+AC_CONFIG_HEADERS([dsieve-config.h])
+AM_INIT_AUTOMAKE
+
+AM_MAINTAINER_MODE
+
+AC_PROG_CC
+AC_PROG_CPP
+AC_PROG_LEX
+AC_PROG_YACC
+AM_PROG_LIBTOOL
+
+AC_ARG_WITH(dovecot,
+[  --with-dovecot[=DIR]    Dovecot base directory (../)],
+	dovecotdir="$withval",
+	dovecotdir=../dovecot
+)
+old=`pwd`
+cd $dovecotdir
+dovecotdir=`pwd`
+cd $old
+AC_SUBST(dovecotdir)
+
+if ! test -f "$dovecotdir/dovecot-config"; then
+  echo
+  echo "dovecot-config not found from $dovecotdir, use --with-dovecot=PATH"
+  echo "to give path to compiled Dovecot sources or to a directory with the"
+  echo "installed dovecot-config file."
+  AC_MSG_ERROR([dovecot-config not found])
+fi
+
+if test -d "$dovecotdir/src"; then
+  # compiling against sources
+  have_dovecot_libs=yes
+else
+  # compiling against installed headers
+  have_dovecot_libs=no
+fi
+AM_CONDITIONAL(HAVE_DOVECOT_LIBS, test "$have_dovecot_libs" = "yes")
+
+dnl replace relative ../ paths in the file with full paths
+eval `cat $dovecotdir/dovecot-config|sed 's,\$(top_builddir)/,$dovecotdir/,g'`
+
+if test $have_dovecot_libs = yes; then
+  dovecot_incdir="$dovecotdir"
+fi
+
+dnl * Regexp library check, from Cyrus IMAP
+AC_SEARCH_LIBS(regcomp, rx regex, [
+  CFLAGS="$CFLAGS -DENABLE_REGEX"
+  AC_CHECK_HEADER(rxposix.h, CFLAGS="$CFLAGS -DHAVE_RX")])
+
+AC_SUBST(STORAGE_LIBS)
+AC_SUBST(LIBICONV)
+AC_SUBST(RAND_LIBS)
+AC_SUBST(MODULE_LIBS)
+AC_SUBST(dovecot_incdir)
+AC_SUBST(moduledir)
+
+AC_CONFIG_FILES([
+Makefile
+src/Makefile
+src/libsieve/Makefile
+stamp.h])
+
+AC_OUTPUT
--- dovecot-1.0.15.orig/dovecot-sieve/AUTHORS
+++ dovecot-1.0.15/dovecot-sieve/AUTHORS
@@ -0,0 +1,2 @@
+Timo Sirainen <tss@iki.fi>
+CMU Sieve people, see src/libsieve/AUTHORS
--- dovecot-1.0.15.orig/dovecot-sieve/src/imparse.h
+++ dovecot-1.0.15/dovecot-sieve/src/imparse.h
@@ -0,0 +1,6 @@
+#ifndef __IMPARSE_H
+#define __IMPARSE_H
+
+extern int imparse_isatom (const char *s);
+
+#endif
--- dovecot-1.0.15.orig/dovecot-sieve/src/map.c
+++ dovecot-1.0.15/dovecot-sieve/src/map.c
@@ -0,0 +1,55 @@
+#include "lib.h"
+#include "map.h"
+
+#include <unistd.h>
+
+static ssize_t read_full_n(int fd, void *data, size_t size)
+{
+	ssize_t ret, all_ret = 0;
+
+	while (size > 0) {
+		ret = read(fd, data, size);
+		if (ret <= 0)
+			return ret;
+
+		data = PTR_OFFSET(data, ret);
+		all_ret += ret;
+		size -= ret;
+	}
+
+	return all_ret;
+}
+
+void map_refresh(int fd, int onceonly __attr_unused__, const char **base,
+		 unsigned long *len, unsigned long newlen,
+		 const char *name, const char *mboxname __attr_unused__)
+{
+	ssize_t ret;
+	void *p;
+
+	if (newlen == 0) {
+		/* the file is a broken zero-byte file */
+		*len = 0;
+		return;
+	}
+
+	*base = p = i_malloc(newlen);
+	*len = newlen;
+
+	ret = read_full_n(fd, p, newlen);
+	if (ret < 0) {
+		i_error("read_full_n(%s) failed: %m", name);
+		ret = 0;
+	}
+
+	*len = ret;
+}
+
+void map_free(const char **base, unsigned long *len __attr_unused__)
+{
+	char *x = (char *) *base;
+
+	i_free(x);
+	*base = NULL;
+}
+
--- dovecot-1.0.15.orig/dovecot-sieve/src/cmusieve-plugin.c
+++ dovecot-1.0.15/dovecot-sieve/src/cmusieve-plugin.c
@@ -0,0 +1,87 @@
+/* Copyright (C) 2006 Timo Sirainen */
+
+#include "lib.h"
+#include "home-expand.h"
+#include "deliver.h"
+#include "cmusieve-plugin.h"
+
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#define SIEVE_SCRIPT_PATH "~/.dovecot.sieve"
+
+static deliver_mail_func_t *next_deliver_mail;
+struct et_list *_et_list = NULL;
+
+static const char *get_sieve_path(void)
+{
+	const char *script_path, *home;
+	struct stat st;
+
+	home = getenv("HOME");
+
+	/* userdb may specify Sieve path */
+	script_path = getenv("SIEVE");
+	if (script_path != NULL) {
+		if (*script_path == '\0') {
+			/* disabled */
+			return NULL;
+		}
+		script_path = home_expand(script_path);
+
+		if (*script_path != '/' && *script_path != '\0') {
+			/* relative path. change to absolute. */
+			script_path = t_strconcat(getenv("HOME"), "/",
+						  script_path, NULL);
+		}
+	} else {
+		if (home == NULL) {
+			i_error("Per-user script path is unknown. See "
+				"http://wiki.dovecot.org/LDA/Sieve#location");
+			return NULL;
+		}
+
+		script_path = home_expand(SIEVE_SCRIPT_PATH);
+	}
+
+	if (stat(script_path, &st) < 0) {
+		if (errno != ENOENT)
+			i_error("stat(%s) failed: %m", script_path);
+
+		/* use global script instead, if one exists */
+		script_path = getenv("SIEVE_GLOBAL_PATH");
+		if (script_path == NULL) {
+			/* for backwards compatibility */
+			script_path = getenv("GLOBAL_SCRIPT_PATH");
+		}
+	}
+
+	return script_path;
+}
+
+static int
+cmusieve_deliver_mail(struct mail_storage *storage, struct mail *mail,
+		      const char *username, const char *mailbox)
+{
+	const char *script_path;
+
+	script_path = get_sieve_path();
+	if (script_path == NULL)
+		return 0;
+
+	if (getenv("DEBUG") != NULL)
+		i_info("cmusieve: Using sieve path: %s", script_path);
+
+	return cmu_sieve_run(storage, mail, script_path, username, mailbox);
+}
+
+void cmusieve_plugin_init(void)
+{
+	next_deliver_mail = deliver_mail;
+	deliver_mail = cmusieve_deliver_mail;
+}
+
+void cmusieve_plugin_deinit(void)
+{
+	deliver_mail = next_deliver_mail;
+}
--- dovecot-1.0.15.orig/dovecot-sieve/src/sieve-cmu.c
+++ dovecot-1.0.15/dovecot-sieve/src/sieve-cmu.c
@@ -0,0 +1,719 @@
+/* Copyright (C) 2005-2006 Timo Sirainen */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "array.h"
+#include "hostpid.h"
+#include "str.h"
+#include "str-sanitize.h"
+#include "write-full.h"
+#include "message-date.h"
+#include "mail-storage.h"
+#include "deliver.h"
+#include "duplicate.h"
+#include "mail-send.h"
+#include "smtp-client.h"
+#include "libsieve/sieve_interface.h"
+#include "cmusieve-plugin.h"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+/* data per script */
+typedef struct script_data {
+	const char *username;
+	struct mail_storage *storage;
+	string_t *errors;
+} script_data_t;
+
+typedef struct {
+	struct mail *mail;
+	const char *mailbox;
+	const char *id;
+	const char *return_path;
+	const char *authuser;
+
+	const char *temp[10];
+} sieve_msgdata_t;
+
+static const char *unfold_header(const char *str)
+{
+	char *new_str;
+	unsigned int i, j;
+
+	for (i = 0; str[i] != '\0'; i++) {
+		if (str[i] == '\n')
+			break;
+	}
+	if (str[i] == '\0')
+		return str;
+
+	/* @UNSAFE */
+	new_str = t_malloc(i + strlen(str+i) + 1);
+	memcpy(new_str, str, i);
+	for (j = i; str[i] != '\0'; i++) {
+		if (str[i] == '\n') {
+			new_str[j++] = ' ';
+			i++;
+			i_assert(str[i] == ' ' || str[i] == '\t');
+		} else {
+			new_str[j++] = str[i];
+		}
+	}
+	new_str[j] = '\0';
+	return new_str;
+}
+
+static const char *const *
+unfold_multiline_headers(const char *const *headers)
+{
+	const char **new_headers;
+	unsigned int i;
+
+	/* see if there are any multiline headers */
+	for (i = 0; headers[i] != NULL; i++) {
+		if (strchr(headers[i], '\n') != NULL)
+			break;
+	}
+	if (headers[i] == NULL) {
+		/* no multilines */
+		return headers;
+	}
+
+	/* @UNSAFE */
+	for (; headers[i] != NULL; i++) ;
+	new_headers = t_new(const char *, i + 1);
+	for (i = 0; headers[i] != NULL; i++)
+		new_headers[i] = unfold_header(headers[i]);
+	return new_headers;
+}
+
+/* gets the header "head" from msg. */
+static int getheader(void *v, const char *phead, const char ***body)
+{
+    sieve_msgdata_t *m = v;
+    const char *const *headers;
+
+    if (phead==NULL) return SIEVE_FAIL;
+    headers = mail_get_headers(m->mail, phead);
+    if (headers != NULL)
+	    headers = unfold_multiline_headers(headers);
+    *body = (const char **)headers;
+
+    if (*body) {
+	return SIEVE_OK;
+    } else {
+	return SIEVE_FAIL;
+    }
+}
+
+static int getsize(void *mc, int *size)
+{
+    sieve_msgdata_t *md = mc;
+    uoff_t psize;
+
+    psize = mail_get_physical_size(md->mail);
+    if (psize == (uoff_t)-1)
+	    return SIEVE_FAIL;
+
+    *size = psize;
+    return SIEVE_OK;
+}
+
+/* we use the temp field in message_data to avoid having to malloc memory
+   to return, and we also can't expose our the receipients to the message */
+static int getenvelope(void *mc, const char *field, const char ***contents)
+{
+    sieve_msgdata_t *m = (sieve_msgdata_t *) mc;
+
+    if (!strcasecmp(field, "from")) {
+	if (m->return_path == NULL) {
+	    /* invalid or missing return path */
+	    *contents = NULL;
+	    return SIEVE_FAIL;
+	}
+	*contents = m->temp;
+	m->temp[0] = m->return_path;
+	m->temp[1] = NULL;
+	return SIEVE_OK;
+    } else if (!strcasecmp(field, "to")) {
+	*contents = m->temp;
+	m->temp[0] = /*FIXME:msg_getrcptall(m, m->cur_rcpt)*/m->authuser;
+	m->temp[1] = NULL;
+	return SIEVE_OK;
+    } else if (!strcasecmp(field, "auth") && m->authuser) {
+	*contents = m->temp;
+	m->temp[0] = m->authuser;
+	m->temp[1] = NULL;
+	return SIEVE_OK;
+    } else {
+	*contents = NULL;
+	return SIEVE_FAIL;
+    }
+}
+
+static int sieve_redirect(void *ac, 
+			  void *ic __attr_unused__, 
+			  void *sc, void *mc, const char **errmsg)
+{
+    sieve_redirect_context_t *rc = (sieve_redirect_context_t *) ac;
+    script_data_t *sd = (script_data_t *) sc;
+    sieve_msgdata_t *m = mc;
+    const char *dupeid;
+    int res;
+
+    /* if we have a msgid, we can track our redirects */
+    dupeid = m->id == NULL ? NULL : t_strdup_printf("%s-%s", m->id, rc->addr);
+    if (dupeid != NULL) {
+	/* ok, let's see if we've redirected this message before */
+	if (duplicate_check(dupeid, strlen(dupeid), sd->username)) {
+	    /*duplicate_log(m->id, sd->username, "redirect");*/
+	    i_info("msgid=%s: discarded duplicate forward to <%s>",
+		   str_sanitize(m->id, 80), str_sanitize(rc->addr, 80));
+            return SIEVE_OK;
+	}
+    }
+
+    if ((res = mail_send_forward(m->mail, rc->addr)) == 0) {
+	/* mark this message as redirected */
+	i_info("msgid=%s: forwarded to <%s>",
+	       m->id == NULL ? "" : str_sanitize(m->id, 80),
+	       str_sanitize(rc->addr, 80));
+        if (dupeid != NULL) {
+            duplicate_mark(dupeid, strlen(dupeid), sd->username,
+                           ioloop_time + DUPLICATE_DEFAULT_KEEP);
+        }
+	return SIEVE_OK;
+    } else {
+	*errmsg = "Error sending mail";
+	return SIEVE_FAIL;
+    }
+}
+
+static int sieve_discard(void *ac __attr_unused__, 
+			 void *ic __attr_unused__, 
+			 void *sc __attr_unused__, void *mc,
+			 const char **errmsg __attr_unused__)
+{
+    sieve_msgdata_t *md = mc;
+
+    /* ok, we won't file it, but log it */
+    i_info("msgid=%s: discarded",
+	   md->id == NULL ? "" : str_sanitize(md->id, 80));
+    return SIEVE_OK;
+}
+
+static int sieve_reject(void *ac, 
+			void *ic __attr_unused__, 
+			void *sc, void *mc, const char **errmsg)
+{
+    sieve_reject_context_t *rc = (sieve_reject_context_t *) ac;
+    script_data_t *sd = (script_data_t *) sc;
+    sieve_msgdata_t *md = (sieve_msgdata_t *) mc;
+    int res;
+
+    if (md->return_path == NULL) {
+	/* return message to who?!? */
+	*errmsg = "No return-path for reply";
+	return SIEVE_FAIL;
+    }
+
+    if (strlen(md->return_path) == 0) {
+        i_info("msgid=%s: discarded reject to <>",
+	       md->id == NULL ? "" : str_sanitize(md->id, 80));
+        return SIEVE_OK;
+    }
+
+    if ((res = mail_send_rejection(md->mail, sd->username, rc->msg)) == 0) {
+        i_info("msgid=%s: rejected",
+	       md->id == NULL ? "" : str_sanitize(md->id, 80));
+	return SIEVE_OK;
+    } else {
+	*errmsg = "Error sending mail";
+	return SIEVE_FAIL;
+    }
+    return SIEVE_FAIL;
+}
+
+static void get_flags(const sieve_imapflags_t *sieve_flags,
+		      enum mail_flags *flags_r, const char *const **keywords_r)
+{
+	array_t ARRAY_DEFINE(keywords, const char *);
+        const char *name;
+	int i;
+
+	*flags_r = 0;
+
+	ARRAY_CREATE(&keywords, default_pool, const char *, 16);
+	for (i = 0; i < sieve_flags->nflags; i++) {
+		name = sieve_flags->flag[i];
+
+		if (name != NULL && *name != '\\') {
+			/* keyword */
+			array_append(&keywords, &name, 1);
+		} else {
+			/* system flag */
+			if (name == NULL || strcasecmp(name, "\\flagged") == 0)
+				*flags_r |= MAIL_FLAGGED;
+			else if (strcasecmp(name, "\\answered") == 0)
+				*flags_r |= MAIL_ANSWERED;
+			else if (strcasecmp(name, "\\deleted") == 0)
+				*flags_r |= MAIL_DELETED;
+			else if (strcasecmp(name, "\\seen") == 0)
+				*flags_r |= MAIL_SEEN;
+			else if (strcasecmp(name, "\\draft") == 0)
+				*flags_r |= MAIL_DRAFT;
+		}
+	}
+
+	name = NULL;
+	array_append(&keywords, &name, 1);
+
+	*keywords_r = array_count(&keywords) == 1 ? NULL :
+		array_get(&keywords, 0);
+}
+
+static int sieve_fileinto(void *ac, 
+			  void *ic __attr_unused__,
+			  void *sc, 
+			  void *mc,
+			  const char **errmsg __attr_unused__)
+{
+    sieve_fileinto_context_t *fc = (sieve_fileinto_context_t *) ac;
+    script_data_t *sd = (script_data_t *) sc;
+    sieve_msgdata_t *md = (sieve_msgdata_t *) mc;
+    enum mail_flags flags;
+    const char *const *keywords;
+
+    get_flags(fc->imapflags, &flags, &keywords);
+
+    if (deliver_save(sd->storage, fc->mailbox, md->mail, flags, keywords) < 0)
+	    return SIEVE_FAIL;
+
+    return SIEVE_OK;
+}
+
+static int sieve_keep(void *ac, 
+		      void *ic __attr_unused__,
+		      void *sc, void *mc, const char **errmsg __attr_unused__)
+{
+    sieve_keep_context_t *kc = (sieve_keep_context_t *) ac;
+    script_data_t *sd = (script_data_t *) sc;
+    sieve_msgdata_t *md = (sieve_msgdata_t *) mc;
+    enum mail_flags flags;
+    const char *const *keywords;
+
+    get_flags(kc->imapflags, &flags, &keywords);
+
+    if (deliver_save(sd->storage, md->mailbox, md->mail, flags, keywords) < 0)
+	    return SIEVE_FAIL;
+
+    return SIEVE_OK;
+}
+
+static bool contains_8bit(const char *msg)
+{
+	const unsigned char *s = (const unsigned char *)msg;
+
+	for (; *s != '\0'; s++) {
+		if ((*s & 0x80) != 0)
+			return TRUE;
+	}
+	return FALSE;
+}
+
+static int sieve_notify(void *ac,
+			void *ic __attr_unused__,
+			void *sc __attr_unused__,
+			void *mc,
+			const char **errmsg)
+{
+    sieve_notify_context_t *nc = (sieve_notify_context_t *) ac;
+    sieve_msgdata_t *m = mc;
+
+    int nopt = 0;
+    FILE *f;
+    struct smtp_client *smtp_client;
+    const char *outmsgid;
+
+    /* "default" is "mailto" as only one... */
+    if (!strcasecmp(nc->method, "default")) nc->method = "mailto";
+    /* check method */
+    if (strcasecmp(nc->method, "mailto")) { 
+        *errmsg = "Unknown [unimplemented] notify method";
+	/* just log error, failed notify is not reason to abort all script. */
+        i_info("SIEVE ERROR: Unknown [unimplemented] notify method <%s>", 
+	nc->method);
+	return SIEVE_OK;
+    }    
+    /* count options */
+    while (nc->options[nopt]) {
+	smtp_client = smtp_client_open(nc->options[nopt], NULL, &f);
+	outmsgid = deliver_get_new_message_id();
+	fprintf(f, "Message-ID: %s\r\n", outmsgid);
+	fprintf(f, "Date: %s\r\n", message_date_create(ioloop_time));
+	fprintf(f, "X-Sieve: %s\r\n", SIEVE_VERSION);
+	if ( strcasecmp(nc->priority, "high") == 0 ) {
+            fprintf(f, "X-Priority: 1 (Highest)\r\n");
+	    fprintf(f, "Importance: High\r\n");
+        } else if ( strcasecmp(nc->priority, "normal") == 0 ) {
+            fprintf(f, "X-Priority: 3 (Normal)\r\n");
+	    fprintf(f, "Importance: Normal\r\n");
+	} else if ( strcasecmp(nc->priority, "low") == 0 ) {
+	    fprintf(f, "X-Priority: 5 (Lowest)\r\n");
+	    fprintf(f, "Importance: Low\r\n");
+	/* RFC: If no importance is given, the default value is "2 (Normal)" */
+	} else {
+	    fprintf(f, "X-Priority: 3 (Normal)\r\n");
+	    fprintf(f, "Importance: Normal\r\n");
+	} 
+	fprintf(f, "From: Postmaster <%s>\r\n",
+		deliver_set->postmaster_address);
+	fprintf(f, "To: <%s>\r\n", nc->options[nopt]);
+	fprintf(f, "Subject: [SIEVE] New mail notification\r\n");
+        fprintf(f, "Auto-Submitted: auto-generated (notify)\r\n");
+	fprintf(f, "Precedence: bulk\r\n");
+        if (contains_8bit(nc->message)) {
+            fprintf(f, "MIME-Version: 1.0\r\n");
+	    fprintf(f, "Content-Type: text/plain; charset=UTF-8\r\n");
+	    fprintf(f, "Content-Transfer-Encoding: 8bit\r\n");
+	}
+	fprintf(f, "\r\n");
+	fprintf(f, "%s\r\n", nc->message);
+	if (smtp_client_close(smtp_client) == 0) {
+		i_info("msgid=%s: sent notification to <%s> (method=%s)",
+		       m->id == NULL ? "" : str_sanitize(m->id, 80),
+		       str_sanitize(nc->options[nopt], 80), nc->method);
+	} else {
+		i_info("msgid=%s: ERROR sending notification to <%s> "
+		       "(method=%s)",
+		       m->id == NULL ? "" : str_sanitize(m->id, 80),
+		       str_sanitize(nc->options[nopt], 80), nc->method);
+		*errmsg = "Error sending notify mail";
+	}
+	nopt = nopt + 1;
+    }
+    return SIEVE_OK;
+}
+
+static int autorespond(void *ac, 
+		       void *ic __attr_unused__,
+		       void *sc,
+		       void *mc,
+		       const char **errmsg __attr_unused__)
+{
+    sieve_autorespond_context_t *arc = (sieve_autorespond_context_t *) ac;
+    script_data_t *sd = (script_data_t *) sc;
+    sieve_msgdata_t *md = mc;
+
+    /* ok, let's see if we've responded before */
+    if (duplicate_check(arc->hash, arc->len,  sd->username)) {
+	i_info("msgid=%s: discarded duplicate vacation response to <%s>",
+	       md->id == NULL ? "" : str_sanitize(md->id, 80),
+	       str_sanitize(md->return_path, 80));
+	return SIEVE_DONE;
+    }
+
+    duplicate_mark(arc->hash, arc->len, sd->username,
+                   ioloop_time + arc->days * (24 * 60 * 60));
+
+    return SIEVE_OK;
+}
+
+static int send_response(void *ac, 
+			 void *ic __attr_unused__, 
+			 void *sc, void *mc,
+			 const char **errmsg)
+{
+    struct smtp_client *smtp_client;
+    FILE *f;
+    const char *outmsgid;
+    sieve_send_response_context_t *src = (sieve_send_response_context_t *) ac;
+    script_data_t *sdata = (script_data_t *) sc;
+    sieve_msgdata_t *md = mc;
+
+    smtp_client = smtp_client_open(src->addr, NULL, &f);
+
+    outmsgid = deliver_get_new_message_id();
+    fprintf(f, "Message-ID: %s\r\n", outmsgid);
+    fprintf(f, "Date: %s\r\n", message_date_create(ioloop_time));
+    
+    fprintf(f, "X-Sieve: %s\r\n", SIEVE_VERSION);
+    fprintf(f, "From: <%s>\r\n", src->fromaddr);
+    fprintf(f, "To: <%s>\r\n", src->addr);
+    fprintf(f, "Subject: %s\r\n", str_sanitize(src->subj, 80));
+    if (md->id) fprintf(f, "In-Reply-To: %s\r\n", md->id);
+    fprintf(f, "Auto-Submitted: auto-replied (vacation)\r\n");
+    fprintf(f, "Precedence: bulk\r\n");
+    fprintf(f, "MIME-Version: 1.0\r\n");
+    if (src->mime) {
+	fprintf(f, "Content-Type: multipart/mixed;"
+		"\r\n\tboundary=\"%s/%s\"\r\n", my_pid, deliver_set->hostname);
+	fprintf(f, "\r\nThis is a MIME-encapsulated message\r\n\r\n");
+	fprintf(f, "--%s/%s\r\n", my_pid, deliver_set->hostname);
+    } else {
+	fprintf(f, "Content-Type: text/plain; charset=utf-8\r\n");
+	fprintf(f, "Content-Transfer-Encoding: 8bit\r\n");
+	fprintf(f, "\r\n");
+    }
+
+    fprintf(f, "%s\r\n", src->msg);
+    if (src->mime)
+	fprintf(f, "\r\n--%s/%s--\r\n", my_pid, deliver_set->hostname);
+
+    if (smtp_client_close(smtp_client) == 0) {
+        duplicate_mark(outmsgid, strlen(outmsgid),
+                       sdata->username, ioloop_time + DUPLICATE_DEFAULT_KEEP);
+	i_info("msgid=%s: sent vacation response to <%s>",
+	       md->id == NULL ? "" : str_sanitize(md->id, 80),
+	       str_sanitize(md->return_path, 80));
+	return SIEVE_OK;
+    } else {
+	*errmsg = "Error sending mail";
+	return SIEVE_FAIL;
+    }
+}
+
+/* vacation support */
+sieve_vacation_t vacation = {
+    1,				/* min response */
+    31,				/* max response */
+    &autorespond,		/* autorespond() */
+    &send_response		/* send_response() */
+};
+
+/* imapflags support */
+static char *markflags[] = { "\\flagged" };
+static sieve_imapflags_t mark = { markflags, 1 };
+
+static int sieve_parse_error_handler(int lineno, const char *msg, 
+				     void *ic __attr_unused__,
+				     void *sc)
+{
+    script_data_t *sd = (script_data_t *) sc;
+
+    if (sd->errors == NULL) {
+	    sd->errors = str_new(default_pool, 1024);
+	    i_info("sieve parse error: line %d: %s", lineno, msg);
+    }
+
+    str_printfa(sd->errors, "line %d: %s\n", lineno, msg);
+    return SIEVE_OK;
+}
+
+static int sieve_execute_error_handler(const char *msg, 
+				       void *ic __attr_unused__,
+				       void *sc __attr_unused__,
+				       void *mc __attr_unused__)
+{
+    i_info("sieve runtime error: %s", msg);
+    return SIEVE_OK;
+}
+ 
+static sieve_interp_t *setup_sieve(void)
+{
+    sieve_interp_t *interp = NULL;
+    int res;
+
+    res = sieve_interp_alloc(&interp, NULL);
+    if (res != SIEVE_OK)
+	i_fatal("sieve_interp_alloc() returns %d\n", res);
+
+    res = sieve_register_redirect(interp, &sieve_redirect);
+    if (res != SIEVE_OK)
+	i_fatal("sieve_register_redirect() returns %d\n", res);
+    res = sieve_register_discard(interp, &sieve_discard);
+    if (res != SIEVE_OK)
+	i_fatal("sieve_register_discard() returns %d\n", res);
+    res = sieve_register_reject(interp, &sieve_reject);
+    if (res != SIEVE_OK)
+	i_fatal("sieve_register_reject() returns %d\n", res);
+    res = sieve_register_fileinto(interp, &sieve_fileinto);
+    if (res != SIEVE_OK)
+	i_fatal("sieve_register_fileinto() returns %d\n", res);
+    res = sieve_register_keep(interp, &sieve_keep);
+    if (res != SIEVE_OK)
+	i_fatal("sieve_register_keep() returns %d\n", res);
+    res = sieve_register_imapflags(interp, &mark);
+    if (res != SIEVE_OK)
+	i_fatal("sieve_register_imapflags() returns %d\n", res);
+    res = sieve_register_notify(interp, &sieve_notify);
+    if (res != SIEVE_OK)
+	i_fatal("sieve_register_notify() returns %d\n", res);
+    res = sieve_register_size(interp, &getsize);
+    if (res != SIEVE_OK)
+	i_fatal("sieve_register_size() returns %d\n", res);
+    res = sieve_register_header(interp, &getheader);
+    if (res != SIEVE_OK)
+	i_fatal("sieve_register_header() returns %d\n", res);
+
+    res = sieve_register_envelope(interp, &getenvelope);
+    if (res != SIEVE_OK)
+	i_fatal("sieve_register_envelope() returns %d\n", res);
+    res = sieve_register_vacation(interp, &vacation);
+    if (res != SIEVE_OK)
+	i_fatal("sieve_register_vacation() returns %d\n", res);
+    res = sieve_register_parse_error(interp, &sieve_parse_error_handler);
+    if (res != SIEVE_OK)
+	i_fatal("sieve_register_parse_error() returns %d\n", res);
+    res = sieve_register_execute_error(interp,  &sieve_execute_error_handler);
+    if (res != SIEVE_OK)
+	i_fatal("sieve_register_execute_error() returns %d\n", res);
+
+    return interp;
+}
+
+static int
+dovecot_sieve_compile(sieve_interp_t *interp, script_data_t *sdata,
+		      const char *script_path, const char *compiled_path)
+{
+	struct stat st, st2;
+	sieve_script_t *script;
+	bytecode_info_t *bc;
+	const char *temp_path;
+	FILE *f;
+	int fd, ret;
+
+	if (stat(script_path, &st) < 0) {
+		if (errno == ENOENT) {
+			if (getenv("DEBUG") != NULL) {
+				i_info("cmusieve: Script not found: %s",
+				       script_path);
+			}
+			return 0;
+		}
+		i_error("stat(%s) failed: %m", script_path);
+		return -1;
+	}
+	if (stat(compiled_path, &st2) < 0) {
+		if (errno != ENOENT) {
+			i_error("stat(%s) failed: %m", script_path);
+			return -1;
+		}
+	} else {
+		if (st.st_mtime <= st2.st_mtime)
+			return 1;
+	}
+
+	/* need to compile */
+	f = fopen(script_path, "r");
+	if (f == NULL) {
+		i_error("fopen(%s) failed: %m", script_path);
+		return -1;
+	}
+
+	ret = sieve_script_parse(interp, f, sdata, &script);
+	if (ret != SIEVE_OK) {
+		if (sdata->errors == NULL) {
+			sdata->errors = str_new(default_pool, 128);
+			str_printfa(sdata->errors, "parse error %d", ret);
+		}
+		return -1;
+	}
+
+	if (sieve_generate_bytecode(&bc, script) < 0) {
+		i_error("sieve_generate_bytecode() failed");
+		return -1;
+	}
+
+	/* write to temp file */
+	temp_path = t_strconcat(compiled_path, ".tmp", NULL);
+	fd = open(temp_path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+	if(fd == -1) {
+		i_error("open(%s) failed: %m", temp_path);
+		return -1;
+	}
+
+	if (sieve_emit_bytecode(fd, bc) < 0) {
+		i_error("sieve_emit_bytecode() failed");
+		return -1;
+	}
+
+	if (close(fd) < 0)
+		i_error("close() failed: %m");
+
+	/* and finally replace the script */
+	if (rename(temp_path, compiled_path) < 0) {
+		i_error("rename(%s, %s) failed: %m", temp_path, compiled_path);
+		return -1;
+	}
+	return 1;
+}
+
+static void
+dovecot_sieve_write_error_file(script_data_t *sdata, const char *path)
+{
+	int fd;
+
+	fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+	if (fd == -1) {
+		i_error("open(%s) failed: %m", path);
+		return;
+	}
+
+	if (write_full(fd, str_data(sdata->errors), str_len(sdata->errors)) < 0)
+		i_error("write_full(%s) failed: %m", path);
+
+	if (close(fd) < 0)
+		i_error("close() failed: %m");
+}
+
+int cmu_sieve_run(struct mail_storage *storage, struct mail *mail,
+		  const char *script_path, const char *username,
+		  const char *mailbox)
+{
+	sieve_interp_t *interp;
+	sieve_bytecode_t *bytecode;
+	script_data_t sdata;
+	sieve_msgdata_t mdata;
+	const char *compiled_path, *path;
+	int ret;
+
+	interp = setup_sieve();
+
+	memset(&sdata, 0, sizeof(sdata));
+	sdata.username = username;
+	sdata.storage = storage;
+
+	compiled_path = t_strconcat(script_path, "c", NULL);
+	ret = dovecot_sieve_compile(interp, &sdata, script_path, compiled_path);
+
+	if (sdata.errors != NULL) {
+		if (getenv("DEBUG") != NULL) {
+			i_info("cmusieve: Compilation failed for %s: %s",
+			       script_path,
+			       str_sanitize(str_c(sdata.errors), 80));
+		}
+		path = t_strconcat(script_path, ".err", NULL);
+		dovecot_sieve_write_error_file(&sdata, path);
+		str_free(&sdata.errors);
+	}
+	if (ret <= 0)
+		return ret;
+
+	memset(&mdata, 0, sizeof(mdata));
+	mdata.mail = mail;
+	mdata.mailbox = mailbox;
+	mdata.authuser = username;
+	mdata.id = mail_get_first_header(mail, "Message-ID");
+	mdata.return_path = deliver_get_return_address(mail);
+
+	if ((ret = sieve_script_load(compiled_path, &bytecode)) != SIEVE_OK) {
+		i_error("sieve_script_load(%s) failed: %d", compiled_path, ret);
+		return -1;
+	}
+
+	if (getenv("DEBUG") != NULL)
+		i_info("cmusieve: Executing script %s", compiled_path);
+
+	if (sieve_execute_bytecode(bytecode, interp,
+				   &sdata, &mdata) != SIEVE_OK) {
+		i_error("sieve_execute_bytecode(%s) failed", compiled_path);
+		return -1;
+	}
+
+	return 1;
+}
--- dovecot-1.0.15.orig/dovecot-sieve/src/map.h
+++ dovecot-1.0.15/dovecot-sieve/src/map.h
@@ -0,0 +1,10 @@
+#ifndef __MAP_H
+#define __MAP_H
+
+extern void map_refresh(int fd, int onceonly, const char **base,
+			unsigned long *len, unsigned long newlen,
+			const char *name, const char *mboxname);
+
+extern void map_free(const char **base, unsigned long *len);
+
+#endif
--- dovecot-1.0.15.orig/dovecot-sieve/src/imparse.c
+++ dovecot-1.0.15/dovecot-sieve/src/imparse.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any other legal
+ *    details, please contact  
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ *
+ */
+#include "imparse.h"
+
+int imparse_isatom(const char *s)
+{
+    int len = 0;
+
+    if (!*s) return 0;
+    for (; *s; s++) {
+	len++;
+	if (*s & 0x80 || *s < 0x1f || *s == 0x7f ||
+	    *s == ' ' || *s == '{' || *s == '(' || *s == ')' ||
+	    *s == '\"' || *s == '%' || *s == '*' || *s == '\\') return 0;
+    }
+    if (len >= 1024) return 0;
+    return 1;
+}
--- dovecot-1.0.15.orig/dovecot-sieve/src/cmusieve-plugin.h
+++ dovecot-1.0.15/dovecot-sieve/src/cmusieve-plugin.h
@@ -0,0 +1,11 @@
+#ifndef __CMUSIEVE_PLUGIN_H
+#define __CMUSIEVE_PLUGIN_H
+
+int cmu_sieve_run(struct mail_storage *storage, struct mail *mail,
+		  const char *script_path, const char *username,
+		  const char *mailbox);
+
+void cmusieve_plugin_init(void);
+void cmusieve_plugin_deinit(void);
+
+#endif
--- dovecot-1.0.15.orig/dovecot-sieve/src/libconfig.h
+++ dovecot-1.0.15/dovecot-sieve/src/libconfig.h
@@ -0,0 +1,8 @@
+#ifndef __LIBCONFIG_H
+#define __LIBCONFIG_H
+
+#define IMAPOPT_RFC3028_STRICT 1
+
+#define config_getswitch(n) 1
+
+#endif
--- dovecot-1.0.15.orig/dovecot-sieve/src/Makefile.am
+++ dovecot-1.0.15/dovecot-sieve/src/Makefile.am
@@ -0,0 +1,32 @@
+SUBDIRS = libsieve
+
+AM_CPPFLAGS = \
+	-I$(dovecot_incdir) \
+	-I$(dovecot_incdir)/src/lib \
+	-I$(dovecot_incdir)/src/lib-dict \
+	-I$(dovecot_incdir)/src/lib-mail \
+	-I$(dovecot_incdir)/src/lib-storage \
+	-I$(dovecot_incdir)/src/deliver
+
+lda_moduledir = $(moduledir)/lda
+
+lib90_cmusieve_plugin_la_LDFLAGS = -module -avoid-version
+
+lda_module_LTLIBRARIES = \
+	lib90_cmusieve_plugin.la
+
+lib90_cmusieve_plugin_la_LIBADD = \
+	libsieve/libsieve.la
+
+lib90_cmusieve_plugin_la_SOURCES = \
+	cmusieve-plugin.c \
+	imparse.c \
+	map.c \
+	sieve-cmu.c
+
+noinst_HEADERS = \
+	cmusieve-plugin.h \
+	imparse.h \
+	libconfig.h \
+	map.h \
+	xmalloc.h
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/bc_eval.c
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/bc_eval.c
@@ -0,0 +1,1178 @@
+/* bc_eval.c - evaluate the bytecode
+ * $Id$
+ */
+/***********************************************************
+        Copyright 2001 by Carnegie Mellon University
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+******************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "sieve_interface.h"
+#include "interp.h"
+#include "message.h"
+
+#include "bytecode.h"
+
+#include "xmalloc.h"
+
+#include <string.h>
+#include <ctype.h>
+
+/**************************************************************************/
+/**************************************************************************/
+/**************************************************************************/
+/**************************EXECUTING BYTECODE******************************/
+/**************************************************************************/
+/**************************************************************************/
+/**************************************************************************/
+/**************************************************************************/
+
+/* Given a bytecode_input_t at the beginning of a string (the len block),
+ * return the string, the length, and the bytecode index of the NEXT
+ * item */
+int unwrap_string(bytecode_input_t *bc, int pos, const char **str, int *len)
+{
+    int local_len = ntohl(bc[pos].value);
+
+    pos++;
+    
+    if(local_len == -1) {
+	/* -1 length indicates NULL */
+	*str = NULL;
+    } else {
+	/* This cast is ugly, but necessary */
+	*str = (const char *)&bc[pos].str;
+	
+	/* Compute the next index */
+	pos += ((ROUNDUP(local_len+1))/sizeof(bytecode_input_t));
+    }
+    
+    if(len) *len = local_len;
+    
+    return pos;
+}
+
+
+/* this is used by notify to pass the options list to do_notify
+ * do_notify needs null-terminated (char *)[],
+ *  we have a stringlist, the beginning of which is pointed at by pos */
+static const char ** bc_makeArray(bytecode_input_t *bc, int *pos)
+{
+    int i;
+    const char** array;
+    int len = ntohl(bc[*pos].value);
+
+    (*pos)+=2; /* Skip # Values and Total Byte Length */
+  
+    array=(const char **)xmalloc((len+1) * sizeof(char *));
+
+    for (i=0; i<len; i++) {
+	*pos = unwrap_string(bc, *pos, &(array[i]), NULL);
+    }
+
+    array[i] = NULL;
+  
+    return array;
+}
+
+/* Compile a regular expression for use during parsing */
+static regex_t * bc_compile_regex(const char *s, int ctag,
+				  char *errmsg, size_t errsiz)
+{
+    int ret;
+    regex_t *reg = (regex_t *) xmalloc(sizeof(regex_t));
+    
+    if ( (ret=regcomp(reg, s, ctag)) != 0)
+    {
+	(void) regerror(ret, reg, errmsg, errsiz);
+	free(reg);
+	return NULL;
+    }
+    return reg;
+}
+
+/* Determine if addr is a system address */
+static int sysaddr(const char *addr)
+{
+    if (!strncasecmp(addr, "MAILER-DAEMON", 13))
+	return 1;
+
+    if (!strncasecmp(addr, "LISTSERV", 8))
+	return 1;
+
+    if (!strncasecmp(addr, "majordomo", 9))
+	return 1;
+
+    if (strstr(addr, "-request@"))
+	return 1;
+
+    if (!strncmp(addr, "owner-", 6))
+	return 1;
+
+    return 0;
+}
+
+/* look for myaddr and myaddrs in the body of a header - return the match */
+static char* look_for_me(char *myaddr, int numaddresses,
+			       bytecode_input_t *bc, int i, const char **body)
+{
+    char *found = NULL;
+    int l;
+    int curra,x ;
+
+    /* loop through each TO header */
+    for (l = 0; body[l] != NULL && !found; l++) {
+	void *data = NULL, *marker = NULL;
+	char *addr;
+	
+	parse_address(body[l], &data, &marker);
+
+	/* loop through each address in the header */
+	while (!found &&
+	       ((addr = get_address(ADDRESS_ALL,&data, &marker, 1))!= NULL)) {
+
+	    if (!strcasecmp(addr, myaddr)) {
+		found = xstrdup(myaddr);
+		break;
+	    }
+
+	    curra=i;
+
+	    for(x=0; x<numaddresses; x++)
+	    {
+		void *altdata = NULL, *altmarker = NULL;
+		char *altaddr;
+		const char *str;
+
+		curra = unwrap_string(bc, curra, &str, NULL);
+		
+		/* is this address one of my addresses? */
+      		parse_address(str, &altdata, &altmarker);
+
+		altaddr = get_address(ADDRESS_ALL, &altdata, &altmarker, 1);
+
+		if (!strcasecmp(addr,altaddr)) {
+		    found=xstrdup(str);
+		    break;
+		}
+
+		free_address(&altdata, &altmarker);
+	    }
+
+	}
+	free_address(&data, &marker);
+    }
+
+    return found;
+}
+
+static char *list_fields[] = {
+    "list-id",
+    "list-help",
+    "list-subscribe",
+    "list-unsubscribe",
+    "list-post",
+    "list-owner",
+    "list-archive",
+    NULL
+};
+ 
+/* Determine if we should respond to a vacation message */
+static int shouldRespond(void * m, sieve_interp_t *interp,
+			 int numaddresses, bytecode_input_t* bc,
+			 int i, char **from, char **to)
+{
+    const char **body;
+    char buf[128];
+    char *myaddr = NULL;
+    int l = SIEVE_OK, j;
+    void *data = NULL, *marker = NULL;
+    char *tmp;
+    int curra, x;
+    char *found=NULL;
+    char *reply_to=NULL;
+  
+    /* Implementations SHOULD NOT respond to any message that contains a
+       "List-Id" [RFC2919], "List-Help", "List-Subscribe", "List-
+       Unsubscribe", "List-Post", "List-Owner" or "List-Archive" [RFC2369]
+       header field. */
+    for (j = 0; list_fields[j]; j++) {
+	strcpy(buf, list_fields[j]);
+	if (interp->getheader(m, buf, &body) == SIEVE_OK) {
+	    l = SIEVE_DONE;
+	    break;
+	}
+    }
+
+    /* Implementations SHOULD NOT respond to any message that has an
+       "Auto-submitted" header field with a value other than "no".
+       This header field is described in [RFC3834]. */
+    strcpy(buf, "auto-submitted");
+    if (interp->getheader(m, buf, &body) == SIEVE_OK) {
+	/* we don't deal with comments, etc. here */
+	/* skip leading white-space */
+	while (*body[0] && isspace((int) *body[0])) body[0]++;
+	if (strcasecmp(body[0], "no")) l = SIEVE_DONE;
+    }
+
+    /* is there a Precedence keyword of "junk | bulk | list"? */
+    /* XXX  non-standard header, but worth checking */
+    strcpy(buf, "precedence");
+    if (interp->getheader(m, buf, &body) == SIEVE_OK) {
+	/* we don't deal with comments, etc. here */
+	/* skip leading white-space */
+	while (*body[0] && isspace((int) *body[0])) body[0]++;
+	if (!strcasecmp(body[0], "junk") ||
+	    !strcasecmp(body[0], "bulk") ||
+	    !strcasecmp(body[0], "list"))
+	    l = SIEVE_DONE;
+    }
+
+    /* Note: the domain-part of all addresses are canonicalized */
+    /* grab my address from the envelope */
+    if (l == SIEVE_OK) {
+	strcpy(buf, "to");
+	l = interp->getenvelope(m, buf, &body);
+	
+	if (body[0]) {  
+	    parse_address(body[0], &data, &marker);
+	    tmp = get_address(ADDRESS_ALL, &data, &marker, 1);
+	    myaddr = (tmp != NULL) ? xstrdup(tmp) : NULL;
+	    free_address(&data, &marker);
+	}  
+    }  
+  
+    if (l == SIEVE_OK) {
+	strcpy(buf, "from");
+	l = interp->getenvelope(m, buf, &body);
+    }
+    if (l == SIEVE_OK && body[0]) {
+	/* we have to parse this address & decide whether we
+	   want to respond to it */
+	parse_address(body[0], &data, &marker);
+	tmp = get_address(ADDRESS_ALL, &data, &marker, 1);
+	reply_to = (tmp != NULL) ? xstrdup(tmp) : NULL;
+	free_address(&data, &marker);
+
+	/* first, is there a reply-to address? */
+	if (reply_to == NULL) {
+	    l = SIEVE_DONE;
+	}
+    
+	/* first, is it from me? */
+	if (l == SIEVE_OK && !strcmp(myaddr, reply_to)) {
+	    l = SIEVE_DONE;
+	}
+   
+	/* ok, is it any of the other addresses i've
+	   specified? */
+	if (l == SIEVE_OK)
+	{
+	    curra=i;
+	    for(x=0; x<numaddresses; x++) {
+		const char *address;
+
+		curra = unwrap_string(bc, curra, &address, NULL);
+		
+		if (!strcmp(address, reply_to))
+		    l=SIEVE_DONE;
+	    }
+	}
+   
+	/* ok, is it a system address? */
+	if (l == SIEVE_OK && sysaddr(reply_to)) {
+	    l = SIEVE_DONE;
+	}
+    }
+    if (l == SIEVE_OK) {
+	/* ok, we're willing to respond to the sender.
+	   but is this message to me?  that is, is my address
+	   in the [Resent]-To, [Resent]-Cc or [Resent]-Bcc fields? */
+	if (strcpy(buf, "to"), 
+	    interp->getheader(m, buf, &body) == SIEVE_OK)
+	    found = look_for_me(myaddr, numaddresses ,bc, i, body);
+	if (!found && (strcpy(buf, "cc"),
+		       (interp->getheader(m, buf, &body) == SIEVE_OK)))
+	    found = look_for_me(myaddr, numaddresses, bc, i, body);
+	if (!found && (strcpy(buf, "bcc"),
+		       (interp->getheader(m, buf, &body) == SIEVE_OK)))
+	    found = look_for_me(myaddr, numaddresses, bc, i, body);
+	if (!found && (strcpy(buf, "resent-to"), 
+		       (interp->getheader(m, buf, &body) == SIEVE_OK)))
+	    found = look_for_me(myaddr, numaddresses ,bc, i, body);
+	if (!found && (strcpy(buf, "resent-cc"),
+		       (interp->getheader(m, buf, &body) == SIEVE_OK)))
+	    found = look_for_me(myaddr, numaddresses, bc, i, body);
+	if (!found && (strcpy(buf, "resent-bcc"),
+		       (interp->getheader(m, buf, &body) == SIEVE_OK)))
+	    found = look_for_me(myaddr, numaddresses, bc, i, body);
+	if (!found)
+	    l = SIEVE_DONE;
+    }
+    /* ok, ok, if we got here maybe we should reply */
+    if (myaddr) free(myaddr);
+    *from=found;
+    *to=reply_to;
+    return l;
+}
+
+/* Evaluate a bytecode test */
+static int eval_bc_test(sieve_interp_t *interp, void* m,
+			bytecode_input_t * bc, int * ip)
+{
+    int res=0; 
+    int i=*ip;
+    int x,y,z;/* loop variable */
+    int list_len; /* for allof/anyof/exists */
+    int list_end; /* for allof/anyof/exists */
+    int address=0;/*to differentiate between address and envelope*/
+    comparator_t * comp=NULL;
+    void * comprock=NULL;
+    int op= ntohl(bc[i].op);
+    
+    switch(op)
+    {
+    case BC_FALSE:
+	res=0; i++; break;
+
+    case BC_TRUE:
+	res=1; i++; break;
+
+    case BC_NOT:/*2*/
+	i+=1;
+	res = eval_bc_test(interp,m, bc, &i);
+	if(res >= 0) res = !res; /* Only invert in non-error case */
+	break;
+
+    case BC_EXISTS:/*3*/
+    {
+	int headersi=i+1;
+	const char** val;
+	int currh;
+
+	res=1;
+
+	list_len=ntohl(bc[headersi].len);
+	list_end=ntohl(bc[headersi+1].value)/4;
+
+	currh=headersi+2;
+
+	for(x=0; x<list_len && res; x++)
+	{
+	    const char *str;
+
+	    currh = unwrap_string(bc, currh, &str, NULL);
+	    
+	    if(interp->getheader(m,str, &val) != SIEVE_OK)
+		res = 0;
+	}
+
+	i=list_end; /* adjust for short-circuit */
+	break;
+    }
+    case BC_SIZE:/*4*/
+    {
+	int s;
+	int sizevar=ntohl(bc[i+1].value);
+	int x=ntohl(bc[i+2].value);
+	
+	if (interp->getsize(m, &s) != SIEVE_OK)
+	    break;
+	
+	if (sizevar ==B_OVER) {
+	    /* over */
+	    res= s > x;
+	} else {
+            /* under */
+	    res= s < x;
+	}
+	i+=3;
+	break;
+    }
+    case BC_ANYOF:/*5*/
+	res = 0;
+	list_len=ntohl(bc[i+1].len);
+	list_end=ntohl(bc[i+2].len)/4;
+	i+=3;
+
+	/* need to process all of them, to ensure our instruction pointer stays
+	 * in the right place */
+	for (x=0; x<list_len && !res; x++) { 
+	    int tmp;
+	    tmp = eval_bc_test(interp,m,bc,&i);
+	    if(tmp < 0) {
+		res = tmp;
+		break;
+	    }
+	    res = res || tmp;
+	}
+
+	i = list_end; /* handle short-circuting */
+
+	break; 
+    case BC_ALLOF:/*6*/ 
+        res = 1;     
+	list_len=ntohl(bc[i+1].len);
+	list_end=ntohl(bc[i+2].len)/4;
+	i+=3;
+
+	/* return 1 unless you find one that isn't true, then return 0 */
+	for (x=0; x<list_len && res; x++) {
+	    int tmp;
+	    tmp = eval_bc_test(interp,m,bc,&i);
+	    if(tmp < 0) {
+		res = tmp;
+		break;
+	    }
+	    res = res && tmp; 
+	}
+
+	i = list_end; /* handle short-circuiting */
+	
+	break;
+    case BC_ADDRESS:/*7*/
+	address=1;
+	/* fall through */
+    case BC_ENVELOPE:/*8*/
+    {
+	const char ** val;
+	void * data=NULL;
+	void * marker=NULL;
+	char * addr;
+	int addrpart=ADDRESS_ALL;/* XXX correct default behavior?*/
+
+ 	int headersi=i+5;/* the i value for the begining of the headers */
+	int datai=(ntohl(bc[headersi+1].value)/4);
+
+	int numheaders=ntohl(bc[headersi].len);
+	int numdata=ntohl(bc[datai].len);
+
+	int currh, currd; /* current header, current data */
+
+	int match=ntohl(bc[i+1].value);
+	int relation=ntohl(bc[i+2].value);
+	int comparator=ntohl(bc[i+3].value);
+	int apart=ntohl(bc[i+4].value);
+	int count=0;
+	char scount[3];
+	int isReg = (match==B_REGEX);
+	int ctag = 0;
+	regex_t *reg;
+	char errbuf[100]; /* Basically unused, as regexps are tested at compile */
+
+	/* set up variables needed for compiling regex */
+	if (isReg)
+	{
+	    if (comparator== B_ASCIICASEMAP)
+	    {
+		ctag = REG_EXTENDED | REG_NOSUB | REG_ICASE;
+	    }
+	    else
+	    {
+		ctag = REG_EXTENDED | REG_NOSUB;
+	    }
+	}
+
+	/*find the correct comparator fcn*/
+	comp = lookup_comp(comparator, match, relation, &comprock);
+
+	if(!comp) {
+	    res = SIEVE_RUN_ERROR;
+	    break;
+	}
+	
+	/*find the part of the address that we want*/
+	switch(apart)
+	{
+	case B_ALL:
+	    addrpart = ADDRESS_ALL; break;
+	case B_LOCALPART:
+	    addrpart = ADDRESS_LOCALPART; break;
+	case B_DOMAIN:
+	    addrpart = ADDRESS_DOMAIN; break;
+	case B_USER:
+	    addrpart = ADDRESS_USER; break;
+	case B_DETAIL:
+	    addrpart = ADDRESS_DETAIL; break;
+	default:
+	    /* this shouldn't happen with correcct bytecode */
+	    res = SIEVE_RUN_ERROR;
+	}
+
+	if(res == SIEVE_RUN_ERROR) break;
+
+	/*loop through all the headers*/
+	currh=headersi+2;
+#if VERBOSE
+	printf("about to process %d headers\n", numheaders);
+#endif
+	for (x=0; x<numheaders && !res; x++)
+	{
+	    const char *this_header;
+
+	    currh = unwrap_string(bc, currh, &this_header, NULL);
+	    
+	    /* Try the next string if we don't have this one */
+	    if(address) {
+		/* Header */
+		if(interp->getheader(m, this_header, &val) != SIEVE_OK)
+		    continue;
+#if VERBOSE
+                printf(" [%d] header %s is %s\n", x, this_header, val[0]);
+#endif
+	    } else {
+		/* Envelope */
+		if(interp->getenvelope(m, this_header, &val) != SIEVE_OK)
+		    continue;
+	    }
+	
+	    /*header exists, now to test it*/
+	    /*search through all the headers that match*/
+	    
+	    for (y=0; val[y]!=NULL && !res; y++) {
+		
+#if VERBOSE
+		printf("about to parse %s\n", val[y]);
+#endif
+		    
+		if (parse_address(val[y], &data, &marker)!=SIEVE_OK) 
+		    return 0;
+		    
+		while (!res &&
+		       (addr = get_address(addrpart, &data, &marker, 0))) {
+#if VERBOSE
+		    printf("working addr %s\n", (addr ? addr : "[nil]"));
+#endif
+			
+		    if (match == B_COUNT) {
+			count++;
+		    } else {
+			/*search through all the data*/ 
+			currd=datai+2;
+			for (z=0; z<numdata && !res; z++)
+			{
+			    const char *data_val;
+			    
+			    currd = unwrap_string(bc, currd, &data_val, NULL);
+
+			    if (isReg) {
+				reg = bc_compile_regex(data_val, ctag,
+						       errbuf, sizeof(errbuf));
+				if (!reg) {
+				    /* Oops */
+				    res=-1;
+				    goto alldone;
+				}
+
+				res |= comp(val[y], (const char *)reg,
+					    comprock);
+				free(reg);
+			    } else {
+#if VERBOSE
+				printf("%s compared to %s(from script)\n",
+				       addr, data_val);
+#endif 
+				res |= comp(addr, data_val, comprock);
+			    }
+			} /* For each data */
+		    }
+		} /* For each address */
+
+		free_address(&data, &marker);
+	    }/* For each message header */
+	    
+#if VERBOSE
+	    printf("end of loop, res is %d, x is %d (%d)\n", res, x, numheaders);
+#endif	    
+	} /* For each script header */
+     
+	if  (match == B_COUNT)
+	{
+	    sprintf(scount, "%u", count);
+	    /* search through all the data */ 
+	    currd=datai+2;
+	    for (z=0; z<numdata && !res; z++)
+	    {
+		const char *data_val;
+		
+		currd = unwrap_string(bc, currd, &data_val, NULL);
+
+		res |= comp(scount, data_val, comprock);
+	    }
+	}
+
+	/* Update IP */
+	i=(ntohl(bc[datai+1].value)/4);
+	
+	break;
+    }
+    case BC_HEADER:/*9*/
+    {
+	const char** val;
+
+	int headersi=i+4;/*the i value for the begining of hte headers*/
+	int datai=(ntohl(bc[headersi+1].value)/4);
+
+	int numheaders=ntohl(bc[headersi].len);
+	int numdata=ntohl(bc[datai].len);
+
+	int currh, currd; /*current header, current data*/
+
+	int match=ntohl(bc[i+1].value);
+	int relation=ntohl(bc[i+2].value);
+	int comparator=ntohl(bc[i+3].value);
+	int count=0;	
+	char scount[3];
+	int isReg = (match==B_REGEX);
+	int ctag = 0;
+	regex_t *reg;
+	char errbuf[100]; /* Basically unused, regexps tested at compile */ 
+
+	/* set up variables needed for compiling regex */
+	if (isReg)
+	{
+	    if (comparator== B_ASCIICASEMAP)
+	    {
+		ctag= REG_EXTENDED | REG_NOSUB | REG_ICASE;
+	    }
+	    else
+	    {
+		ctag= REG_EXTENDED | REG_NOSUB;
+	    }
+     
+	}
+	
+	/*find the correct comparator fcn*/
+	comp=lookup_comp(comparator, match, relation, &comprock);
+
+	if(!comp) {
+	    res = SIEVE_RUN_ERROR;
+	    break;
+	}
+
+	/*search through all the flags for the header*/
+	currh=headersi+2;
+	for(x=0; x<numheaders && !res; x++)
+	{
+	    const char *this_header;
+	    
+	    currh = unwrap_string(bc, currh, &this_header, NULL);
+	   
+	    if(interp->getheader(m, this_header, &val) != SIEVE_OK) {
+		continue; /*this header does not exist, search the next*/ 
+	    }
+#if VERBOSE
+	    printf ("val %s %s %s\n", val[0], val[1], val[2]);
+#endif
+	    
+	    /* search through all the headers that match */
+	    
+	    for (y=0; val[y]!=NULL && !res; y++)
+	    {
+		if  (match == B_COUNT) {
+		    count++;
+		} else {
+		    /*search through all the data*/ 
+		    currd=datai+2;
+		    for (z=0; z<numdata && !res; z++)
+		    {
+			const char *data_val;
+			
+			currd = unwrap_string(bc, currd, &data_val, NULL);
+			
+			if (isReg) {
+			    reg= bc_compile_regex(data_val, ctag, errbuf,
+						  sizeof(errbuf));
+			    if (!reg)
+			    {
+				/* Oops */
+				res=-1;
+				goto alldone;
+			    }
+			    
+			    res |= comp(val[y], (const char *)reg,
+					comprock);
+			    free(reg);
+			} else {
+			    res |= comp(val[y], data_val, comprock);
+			}
+		    }
+		}
+	    }
+	}
+	
+	if  (match == B_COUNT )
+	{
+	    sprintf(scount, "%u", count);
+	    /*search through all the data*/ 
+	    currd=datai+2;
+	    for (z=0; z<numdata && !res; z++)
+	    { 	
+		const char *data_val;
+			
+		currd = unwrap_string(bc, currd, &data_val, NULL);
+#if VERBOSE
+		printf("%d, %s \n", count, data_val);
+#endif
+		res |= comp(scount, data_val, comprock);
+	    }
+	      
+	}
+
+	/* Update IP */
+	i=(ntohl(bc[datai+1].value)/4);
+	
+	break;
+    }
+    default:
+#if VERBOSE
+	printf("WERT, can't evaluate if statement. %d is not a valid command",
+	       op);
+#endif     
+	return SIEVE_RUN_ERROR;
+    }
+    
+  
+ alldone:
+    
+    *ip=i;
+    return res;
+}
+
+/* The entrypoint for bytecode evaluation */
+int sieve_eval_bc(sieve_interp_t *i, const void *bc_in, unsigned int bc_len,
+		  void *m, sieve_imapflags_t * imapflags,
+		  action_list_t *actions,
+		  notify_list_t *notify_list,
+		  const char **errmsg)
+{
+    const char *data;
+    int ip = 0, ip_max = (bc_len/sizeof(bytecode_input_t));
+    int res=0;
+    int op;
+    int version;
+    
+    bytecode_input_t *bc = (bytecode_input_t *)bc_in;
+    
+    /* Check that we
+     * a) have bytecode
+     * b) it is atleast long enough for the magic number, the version
+     *    and one opcode */
+    if(!bc) return SIEVE_FAIL;
+    if(bc_len < (BYTECODE_MAGIC_LEN + 2*sizeof(bytecode_input_t)))
+       return SIEVE_FAIL;
+
+    if(memcmp(bc, BYTECODE_MAGIC, BYTECODE_MAGIC_LEN)) {
+	*errmsg = "Not a bytecode file";
+	return SIEVE_FAIL;
+    }
+
+    ip = BYTECODE_MAGIC_LEN / sizeof(bytecode_input_t);
+
+    version= ntohl(bc[ip].op);
+
+    /* this is because there was a time where integers were not network byte
+       order.  all the scripts written then would have version 0x01 written
+       in host byte order.*/
+
+     if(version == (int)ntohl(1)) {
+	if(errmsg) {
+	    *errmsg =
+		"Incorrect Bytecode Version, please recompile (use sievec)";
+	    
+	}
+	return SIEVE_FAIL;
+    }
+    
+    if( version != BYTECODE_VERSION) {
+	if(errmsg) {
+	    *errmsg =
+		"Incorrect Bytecode Version, please recompile (use sievec)";
+	}
+	return SIEVE_FAIL;
+    }
+
+#if VERBOSE
+    printf("version number %d\n",version); 
+#endif
+
+    for(ip++; ip<ip_max; ) { 
+	op=ntohl(bc[ip].op);
+	switch(op) {
+	case B_STOP:/*0*/
+	    res=1;
+	    break;
+
+	case B_KEEP:/*1*/
+	    res = do_keep(actions, imapflags);
+	    if (res == SIEVE_RUN_ERROR)
+		*errmsg = "Keep can not be used with Reject";
+	    ip++;
+	    break;
+
+	case B_DISCARD:/*2*/
+	    res=do_discard(actions);
+	    ip++;
+	    break;
+
+	case B_REJECT:/*3*/
+	    ip = unwrap_string(bc, ip+1, &data, NULL);
+	    
+	    res = do_reject(actions, data);
+	
+	    if (res == SIEVE_RUN_ERROR)
+		*errmsg = "Reject can not be used with any other action";  
+
+	    break;
+
+	case B_FILEINTO:/*4*/
+	{
+	    ip = unwrap_string(bc, ip+1, &data, NULL);
+
+	    res = do_fileinto(actions, data, imapflags);
+
+	    if (res == SIEVE_RUN_ERROR)
+		*errmsg = "Fileinto can not be used with Reject";
+
+	    break;
+	}
+
+	case B_REDIRECT:/*5*/
+	{
+	    ip = unwrap_string(bc, ip+1, &data, NULL);
+
+	    res = do_redirect(actions, data);
+
+	    if (res == SIEVE_RUN_ERROR)
+		*errmsg = "Redirect can not be used with Reject";
+
+	    break;
+	}
+
+	case B_IF:/*6*/
+	{
+	    int testend=ntohl(bc[ip+1].value);
+	    int result;
+	   
+	    ip+=2;
+	    result=eval_bc_test(i, m, bc, &ip);
+	    
+	    if (result<0) {
+		*errmsg = "Invalid test";
+		return SIEVE_FAIL;
+	    } else if (result) {
+	    	/*skip over jump instruction*/
+		testend+=2;
+	    }
+	    ip=testend;
+	    
+	    break;
+	}
+
+	case B_MARK:/*8*/
+	    res = do_mark(actions);
+	    ip++;
+	    break;
+
+	case B_UNMARK:/*9*/
+	    res = do_unmark(actions);
+	    ip++;
+	    break;
+
+	case B_ADDFLAG:/*10*/ 
+	{
+	    int x;
+	    int list_len=ntohl(bc[ip+1].len);
+
+	    ip+=3; /* skip opcode, list_len, and list data len */
+
+	    for (x=0; x<list_len; x++) {
+		ip = unwrap_string(bc, ip, &data, NULL);
+		
+		res = do_addflag(actions, data);
+
+		if (res == SIEVE_RUN_ERROR)
+		    *errmsg = "addflag can not be used with Reject";
+	    } 
+	    break;
+	}
+
+	case B_SETFLAG:
+	{
+	    int x;
+	    int list_len=ntohl(bc[ip+1].len);
+
+	    ip+=3; /* skip opcode, list_len, and list data len */
+
+	    ip = unwrap_string(bc, ip, &data, NULL);
+
+	    res = do_setflag(actions, data);
+
+	    if (res == SIEVE_RUN_ERROR) {
+		*errmsg = "setflag can not be used with Reject";
+	    } else {
+		for (x=1; x<list_len; x++) {
+		    ip = unwrap_string(bc, ip, &data, NULL);
+
+		    res = do_addflag(actions, data);
+
+		    if (res == SIEVE_RUN_ERROR)
+			*errmsg = "setflag can not be used with Reject";
+		} 
+	    }
+	    
+	    break;
+	}
+
+	case B_REMOVEFLAG:
+	{
+	    int x;
+	    int list_len=ntohl(bc[ip+1].len);
+
+	    ip+=3; /* skip opcode, list_len, and list data len */
+
+	    for (x=0; x<list_len; x++) {
+		ip = unwrap_string(bc, ip, &data, NULL);
+
+		res = do_removeflag(actions, data);
+
+		if (res == SIEVE_RUN_ERROR)
+		    *errmsg = "removeflag can not be used with Reject";
+	    } 
+	    break;
+	}
+
+	case B_NOTIFY:
+	{
+	    const char * id;
+	    const char * method;
+	    const char **options = NULL;
+	    const char *priority = NULL;
+	    const char * message;
+	    int pri;
+	    
+	    ip++;
+
+	    /* method */
+	    ip = unwrap_string(bc, ip, &method, NULL);
+
+	    /* id */
+	    ip = unwrap_string(bc, ip, &id, NULL);
+
+	    /*options*/
+	    options=bc_makeArray(bc, &ip); 
+
+	    /* priority */
+	    pri=ntohl(bc[ip].value);
+	    ip++;
+	    
+	    switch (pri)
+	    {
+	    case B_LOW:
+		priority="low";
+	    case B_NORMAL:
+		priority="normal";
+		break;
+	    case B_HIGH: 
+		priority="high";
+		break; 
+	    case B_ANY:
+		priority="any";
+		break;
+	    default:
+		res=SIEVE_RUN_ERROR;
+	    }
+
+	    /* message */
+	    ip = unwrap_string(bc, ip, &message, NULL);
+	  
+	    res = do_notify(notify_list, id, method, options,
+			    priority, message);
+
+	    break;
+	}
+	case B_DENOTIFY:
+	{
+         /*
+	  * i really have no idea what the count matchtype should do here.
+	  * the sanest thing would be to use 1.
+	  * however that would require passing on the match type to do_notify.
+	  *  -jsmith2
+	  */
+
+	    comparator_t *comp = NULL;
+	    
+	    const char *pattern;
+	    regex_t *reg;
+	    
+	    const char *priority = NULL;
+	    void *comprock = NULL;
+	    
+	    int comparator;
+	    int pri;
+	    
+	    ip++;
+	    pri=ntohl(bc[ip].value);
+	    ip++;
+	    
+	    switch (pri)
+	    {
+	    case B_LOW:
+		priority="low";		
+	    case B_NORMAL:
+		priority="normal";
+		break;
+	    case B_HIGH: 
+		priority="high";
+		break; 
+	    case B_ANY:
+		priority="any";
+		break;
+	    default:
+		res=SIEVE_RUN_ERROR;
+	    }
+
+	    if(res == SIEVE_RUN_ERROR)
+		break;
+	   
+	    comparator =ntohl( bc[ip].value);
+	    ip++;
+	    
+	    if (comparator == B_ANY)
+	    { 
+		ip++;/* skip placeholder this has no comparator function */
+		comp=NULL;
+	    } else {
+		int x= ntohl(bc[ip].value);
+		ip++;
+		
+		comp=lookup_comp(B_ASCIICASEMAP,comparator,
+				 x, &comprock);
+	    }
+	    
+	    ip = unwrap_string(bc, ip, &pattern, NULL);
+	  
+	    if (comparator == B_REGEX)
+	    {	
+		char errmsg[1024]; /* Basically unused */
+		
+		reg=bc_compile_regex(pattern,
+				     REG_EXTENDED | REG_NOSUB | REG_ICASE,
+				     errmsg, sizeof(errmsg));
+		if (!reg) {
+		    res = SIEVE_RUN_ERROR;
+		} else {
+		    res = do_denotify(notify_list, comp, reg,
+				      comprock, priority);
+		    free(reg);
+		}
+	    } else {
+		res = do_denotify(notify_list, comp, pattern,
+				  comprock, priority);
+	    }
+	    
+	    break;
+	}
+	case B_VACATION:
+	{
+	    int respond;
+	    char *fromaddr = NULL; /* relative to message we send */
+	    char *toaddr = NULL; /* relative to message we send */
+	    const char *message = NULL;
+	    char buf[128];
+	    char subject[1024];
+	    int x;
+	    
+	    ip++;
+
+	    x=ntohl( bc[ip].len);
+	    
+	    respond=shouldRespond(m, i, x, bc, ip+2,
+				  &fromaddr, &toaddr);
+	    
+	    ip=(ntohl(bc[ip+1].value)/4);	
+	    if (respond==SIEVE_OK)
+	    {	 
+		ip = unwrap_string(bc, ip, &data, NULL);
+		
+		if (!data) 
+		{
+		    /* we have to generate a subject */
+		    const char **s;	    
+		    strlcpy(buf, "subject", sizeof(buf));
+		    if (i->getheader(m, buf, &s) != SIEVE_OK ||
+			s[0] == NULL) {
+			strlcpy(subject, "Automated reply", sizeof(subject));
+		    } else {
+			/* s[0] contains the original subject */
+			const char *origsubj = s[0];
+
+			snprintf(subject, sizeof(subject), "Auto: %s", origsubj);
+		    }
+		} else {
+		    /* user specified subject */
+		    strlcpy(subject, data, sizeof(subject));
+		}
+		
+		ip = unwrap_string(bc, ip, &message, NULL);
+
+		res = do_vacation(actions, toaddr, fromaddr,
+				  xstrdup(subject), message,
+				  ntohl(bc[ip].value), ntohl(bc[ip+1].value));
+
+		ip+=2;		
+
+		if (res == SIEVE_RUN_ERROR)
+		    *errmsg = "Vacation can not be used with Reject or Vacation";
+	    } else if (respond == SIEVE_DONE) {
+                /* skip subject and message */
+
+		ip = unwrap_string(bc, ip, &data, NULL);
+		ip = unwrap_string(bc, ip, &data, NULL);
+
+		ip+=2;/*skip days and mime flag*/
+	    } else {
+		res = SIEVE_RUN_ERROR; /* something is bad */ 
+	    }
+
+	    break;
+	}
+	case B_NULL:/*15*/
+	    ip++;
+	    break;
+
+	case B_JUMP:/*16*/
+	    ip= ntohl(bc[ip+1].jump);
+	    break;
+	    
+	default:
+	    if(errmsg) *errmsg = "Invalid sieve bytecode";
+	    return SIEVE_FAIL;
+	}
+      
+	if (res) /* we've either encountered an error or a stop */
+	    break;
+    }
+    return res;      
+}
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/bytecode.h
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/bytecode.h
@@ -0,0 +1,193 @@
+/* bytecode.h -- bytecode definition
+ */
+/***********************************************************
+        Copyright 1999 by Carnegie Mellon University
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*****************************************************************/
+
+#ifndef SIEVE_BYTECODE_H
+#define SIEVE_BYTECODE_H
+
+
+/* for debugging*/
+#define DUMPCODE 0
+#define VERBOSE 0
+
+/*for finding correctly aligned bytes on strings*/
+/* bump to the next multiple of 4 bytes */
+#define ROUNDUP(num) (((num) + 3) & 0xFFFFFFFC)
+
+
+/* yes, lots of these are superfluous, it's for clarity */
+typedef union 
+{
+    int op; /* OPTYPE */
+    int value;
+
+    int jump;
+
+    int listlen;
+
+    /* store strings (need 2 consecutive bytecodes) */
+    int len;
+    char *str;
+} bytecode_t;
+
+/* For sanity during input on 64-bit platforms.
+ * str should only be accessed as (char *)&str, but given the use of
+ * unwrap_string, this should be OK */
+typedef union 
+{
+    int op; /* OPTYPE */
+    int value;
+
+    int jump;
+
+    int listlen;
+
+    /* store strings (need 2 consecutive bytecodes) */
+    int len;
+    int str;
+} bytecode_input_t;
+
+
+   /*version 0x01 scripts were written in host byte order.
+   we don't want to use this version number again and cause a mess
+   this isn't a huge concern, since this is version ntohl(1), or 16777216*/
+#define BYTECODE_VERSION 0x03
+#define BYTECODE_MAGIC "CyrSBytecode"
+#define BYTECODE_MAGIC_LEN 12 /* Should be multiple of 4 */
+
+/* IMPORTANT: To maintain forward compatibility of bytecode, please only add
+ * new instructions to the end of these enums.  (The reason these values
+ * are all duplicated here is to avoid silliness if this caveat is forgotten
+ * about in the other tables.) */
+enum bytecode {
+    B_STOP,
+
+    B_KEEP,
+    B_DISCARD,
+    B_REJECT,/* require reject*/
+    B_FILEINTO,/*require fileinto*/
+    B_REDIRECT,
+
+    B_IF,
+  
+    B_MARK,/* require imapflags */
+    B_UNMARK,/* require imapflags */
+
+    B_ADDFLAG,/* require imapflags */
+    B_SETFLAG,/* require imapflags */
+    B_REMOVEFLAG,/* require imapflags */
+
+    B_NOTIFY,/* require notify */
+    B_DENOTIFY,/* require notify */
+
+    B_VACATION,/* require vacation*/
+    B_NULL,
+    B_JUMP
+};
+
+enum bytecode_comps {
+    BC_FALSE,
+    BC_TRUE,
+    BC_NOT,
+    BC_EXISTS,
+    BC_SIZE,
+    BC_ANYOF,
+    BC_ALLOF,
+    BC_ADDRESS,
+    BC_ENVELOPE,  /* require envelope*/
+    BC_HEADER    
+};
+
+/* currently one enum so as to help determine where values are being misused.
+ * we have left placeholders incase we need to add more later to the middle */
+enum bytecode_tags {
+    /* Sizes */
+    B_OVER,
+    B_UNDER,
+
+    B_SIZE_PLACEHOLDER_1,
+    B_SIZE_PLACEHOLDER_2,
+     
+    /* sizes, pt 2 */
+    B_GT, /* require relational*/
+    B_GE,  /* require relational*/
+    B_LT,  /* require relational*/
+    B_LE,  /* require relational*/
+    B_EQ,  /* require relational*/
+    B_NE,  /* require relational*/
+ 
+    B_RELATIONAL_PLACEHOLDER_1,
+    B_RELATIONAL_PLACEHOLDER_2,
+   
+    /* priorities */
+    B_LOW,
+    B_NORMAL,
+    B_HIGH,
+    B_ANY,
+
+    B_PRIORITY_PLACEHOLDER_1,
+    B_PRIORITY_PLACEHOLDER_2,
+    B_PRIORITY_PLACEHOLDER_3,
+    B_PRIORITY_PLACEHOLDER_4,
+    
+    /* Address Part Tags */
+    B_ALL,
+    B_LOCALPART,
+    B_DOMAIN,
+    B_USER,  /* require subaddress */
+    B_DETAIL, /* require subaddress */
+    
+    B_ADDRESS_PLACEHOLDER_1,
+    B_ADDRESS_PLACEHOLDER_2,
+    B_ADDRESS_PLACEHOLDER_3,
+    B_ADDRESS_PLACEHOLDER_4,
+
+    /* Comparators */
+    B_ASCIICASEMAP,
+    B_OCTET,
+    B_ASCIINUMERIC, /* require comparator-i;ascii-numeric */
+    
+    B_COMPARATOR_PLACEHOLDER_1,
+    B_COMPARATOR_PLACEHOLDER_2,
+    B_COMPARATOR_PLACEHOLDER_3,
+    B_COMPARATOR_PLACEHOLDER_4,
+ 
+    /* match types */
+    B_IS,
+    B_CONTAINS,
+    B_MATCHES,
+    B_REGEX,/* require regex*/
+    B_COUNT,/* require relational*/
+    B_VALUE,/* require relational*/
+
+    B_MATCH_PLACEHOLDER_1,
+    B_MATCH_PLACEHOLDER_2,
+    B_MATCH_PLACEHOLDER_3,
+    B_MATCH_PLACEHOLDER_4
+  
+};
+
+int unwrap_string(bytecode_input_t *bc, int pos, const char **str, int *len);
+
+#endif
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/comparator.c
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/comparator.c
@@ -0,0 +1,448 @@
+/* comparator.c -- comparator functions
+ * Larry Greenfield
+ * $Id$
+ */
+/***********************************************************
+        Copyright 1999 by Carnegie Mellon University
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+******************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "xmalloc.h"
+#include "comparator.h"
+#include "tree.h"
+#include "sieve.h"
+#include "bytecode.h"
+
+/*!!! uses B_CONTAINS not CONTAINS, etc, only works with bytecode*/
+
+extern int strcasecmp(const char *, const char *);
+
+typedef int (*compare_t)(const void *, const void *);
+
+/* --- relational comparators --- */
+
+/* these are generic wrappers in which 'rock' is the compare function */
+
+static int rel_eq(const char *text, const char *pat, void *rock)
+{
+    compare_t compar = (compare_t) rock;
+
+    return (compar(text, pat) == 0);
+}
+
+static int rel_ne(const char *text, const char *pat, void *rock)
+{
+    compare_t compar = (compare_t) rock;
+
+    return (compar(text, pat) != 0);
+}
+
+static int rel_gt(const char *text, const char *pat, void *rock)
+{
+    compare_t compar = (compare_t) rock;
+
+    return (compar(text, pat) > 0);
+}
+
+static int rel_ge(const char *text, const char *pat, void *rock)
+{
+    compare_t compar = (compare_t) rock;
+
+    return (compar(text, pat) >= 0);
+}
+
+static int rel_lt(const char *text, const char *pat, void *rock)
+{
+    compare_t compar = (compare_t) rock;
+
+    return (compar(text, pat) < 0);
+}
+
+static int rel_le(const char *text, const char *pat, void *rock)
+{
+    compare_t compar = (compare_t) rock;
+
+    return (compar(text, pat) <= 0);
+}
+
+/* --- i;octet comparators --- */
+
+/* just compare the two; these should be NULL terminated */
+static int octet_cmp(const char *text, const char *pat)
+{
+    size_t sl;
+    int r;
+
+    sl = strlen(text) < strlen(pat) ? strlen(text) : strlen(pat);
+
+    r = memcmp(text, pat, sl);
+
+    if (r == 0)
+	return (strlen(text) - strlen(pat));
+    else 
+	return r;
+}
+
+/* we implement boyer-moore for hell of it, since this is probably
+ not very useful for sieve */
+#if 0
+int boyer_moore(char *text, char *pat)
+{
+    int i, j; /* indexes */
+    int M = strlen(pat); /* length of pattern */
+    int N = strlen(text); /* length of text */
+    int skip[256]; /* table of how much to skip, based on each character */
+
+    /* initialize skip table */
+    for (i = 0; i < 256; i++)
+	skip[i] = M;
+    for (i = 0; i < M; i++)
+	skip[(int) pat[i]] = M-i-1;
+    
+    /* look for pat in text */
+    i = j = M-1;
+    do {
+	if (pat[j] == text[i]) {
+	    i--;
+	    j--;
+	} else {
+	    if (M-j > skip[(int) text[i]]) {
+		i = i + M - j;
+	    } else {
+		i = i + skip[(int) text[i]];
+	    }
+	    j = M-1;
+	}
+    } while (!((j < 0) || (i >= N)));
+    /* i+1 is the position of the match if i < N */
+    return (i < N) ? 1 : 0;
+}
+#endif
+
+/* we do a brute force attack */
+static int octet_contains(const char *text, const char *pat, 
+                          void *rock __attr_unused__)
+{
+    return (strstr(text, pat) != NULL);
+}
+
+static int octet_matches_(const char *text, const char *pat, int casemap)
+{
+    const char *p;
+    const char *t;
+    char c;
+
+    t = text;
+    p = pat;
+    for (;;) {
+	if (*p == '\0') {
+	    /* ran out of pattern */
+	    return (*t == '\0');
+	}
+	c = *p++;
+	switch (c) {
+	case '?':
+	    if (*t == '\0') {
+		return 0;
+	    }
+	    t++;
+	    break;
+	case '*':
+	    while (*p == '*' || *p == '?') {
+		if (*p == '?') {
+		    /* eat the character now */
+		    if (*t == '\0') {
+			return 0;
+		    }
+		    t++;
+		}
+		/* coalesce into a single wildcard */
+		p++;
+	    }
+	    if (*p == '\0') {
+		/* wildcard at end of string, any remaining text is ok */
+		return 1;
+	    }
+
+	    while (*t != '\0') {
+		/* recurse */
+		if (octet_matches_(t, p, casemap)) return 1;
+		t++;
+	    }
+	case '\\':
+	    p++;
+	    /* falls through */
+	default:
+	    if (casemap && (toupper(c) == toupper(*t))) {
+		t++;
+	    } else if (!casemap && (c == *t)) {
+		t++;
+	    } else {
+		/* literal char doesn't match */
+		return 0;
+	    }
+	}
+    }
+    /* never reaches */
+    abort();
+}
+
+static int octet_matches(const char *text, const char *pat, 
+                         void *rock __attr_unused__)
+{
+    return octet_matches_(text, pat, 0);
+}
+
+
+#ifdef ENABLE_REGEX
+static int octet_regex(const char *text, const char *pat, 
+                       void *rock __attr_unused__)
+{
+    return (!regexec((regex_t *) pat, text, 0, NULL, 0));
+}
+#endif
+
+
+/* --- i;ascii-casemap comparators --- */
+
+
+/* use strcasecmp() as the compare function */
+
+/* sheer brute force */
+static int ascii_casemap_contains(const char *text, const char *pat,
+				  void *rock __attr_unused__)
+{
+    int N = strlen(text);
+    int M = strlen(pat);
+    int i, j;
+
+    i = 0, j = 0;
+    while ((j < M) && (i < N)) {
+              if (toupper(text[i]) == toupper(pat[j])) {
+	  	  i++; j++;
+	} else {
+	    i = i - j + 1;
+	    j = 0;
+	}
+    }    
+
+    return (j == M); /* we found a match! */
+}
+
+static int ascii_casemap_matches(const char *text, const char *pat, 
+                                 void *rock __attr_unused__)
+{
+    return octet_matches_(text, pat, 1);
+}
+
+/* i;ascii-numeric; only supports relational tests
+ *
+ *  A \ B    number   not-num 
+ *  number   A ? B    B > A 
+ *  not-num  A > B    A == B
+ */
+
+ /* From RFC 2244:
+  *
+  * The i;ascii-numeric comparator interprets strings as decimal
+  * positive integers represented as US-ASCII digits.  All values
+  * which do not begin with a US-ASCII digit are considered equal
+  * with an ordinal value higher than all non-NIL single-valued
+  * attributes.  Otherwise, all US-ASCII digits (octet values
+  * 0x30 to 0x39) are interpreted starting from the beginning of
+  * the string to the first non-digit or the end of the string.
+  */
+
+static int ascii_numeric_cmp(const char *text, const char *pat)
+{
+    unsigned text_digit_len;
+    unsigned pat_digit_len;
+
+    if (isdigit((int) *pat)) {
+	if (isdigit((int) *text)) {
+	    /* Count how many digits each string has */
+	    for (text_digit_len = 0;
+		 isdigit((int) text[text_digit_len]);
+		 text_digit_len++);
+	    for (pat_digit_len = 0;
+		 isdigit((int) pat[pat_digit_len]);
+		 pat_digit_len++);
+
+	    if (text_digit_len < pat_digit_len) {
+		/* Pad "text" with leading 0s */
+		while (pat_digit_len > text_digit_len) {
+		    /* "text" can only be less or equal to "pat" */
+		    if ('0' < *pat) {
+			return (-1); 
+		    }
+		    pat++;
+		    pat_digit_len--;
+		}
+	    } else if (text_digit_len > pat_digit_len) {
+		/* Pad "pad" with leading 0s */
+		while (text_digit_len > pat_digit_len) {
+		    /* "pad" can only be greater or equal to "text" */
+		    if (*text > '0') {
+			return 1;
+		    }
+		    text++;
+		    text_digit_len--;
+		}
+	    }
+
+	    /* CLAIM: If we here, we have two non-empty digital suffixes
+	       of equal length */
+	    while (text_digit_len > 0) {
+		if (*text < *pat) {
+			return -1;
+		} else if (*text > *pat) {
+			return 1;
+		}
+		/* Characters are equal, carry on */
+		text++;
+		pat++;
+		text_digit_len--;
+	    }
+
+	    return (0);
+	} else {
+	    return 1;
+	}
+    } else if (isdigit((int) *text)) {
+	return -1;
+    } else {
+	return 0; /* both not digits */
+    }
+}
+
+static comparator_t *lookup_rel(int relation)
+{
+    comparator_t *ret;
+
+    ret = NULL;
+    switch (relation)
+      {
+      case B_EQ:
+	ret = &rel_eq;
+	break;
+      case B_NE:
+	ret = &rel_ne; 
+	break;
+      case B_GT: 
+	ret = &rel_gt; 
+	break;
+      case B_GE:
+         ret = &rel_ge; 
+	 break;
+      case B_LT:
+	ret = &rel_lt; 
+	break;
+      case B_LE:
+	ret = &rel_le; 
+      }
+
+    return ret;
+}
+
+comparator_t *lookup_comp(int comp, int mode, int relation,
+			  void **comprock)
+{
+    comparator_t *ret;
+
+    ret = NULL;
+    *comprock = NULL;
+#if VERBOSE
+    printf("comp%d mode%d relat%d     \n", comp, mode, relation); 
+#endif
+    switch (comp)
+      {
+      case B_OCTET:    
+ 	switch (mode) {
+	  case B_IS:
+	    ret = &rel_eq;
+	    *comprock = (void **) &octet_cmp;
+	    break;
+	  case B_CONTAINS:
+	    ret = &octet_contains;
+	    break;
+	  case B_MATCHES:
+	    ret = &octet_matches;
+	    break;
+#ifdef ENABLE_REGEX
+	  case B_REGEX:
+	    ret = &octet_regex;
+	    break;
+#endif
+	  case B_VALUE:
+	    ret = lookup_rel(relation);
+	    *comprock = (void **) &octet_cmp;
+	    break;
+	}
+	break; /*end of octet */
+      case B_ASCIICASEMAP:
+     	switch (mode) {
+	case B_IS:
+	    ret = &rel_eq;
+	    *comprock = (void **) &strcasecmp;
+	    break;
+	case B_CONTAINS:
+	    ret = &ascii_casemap_contains;
+	    break;
+	case B_MATCHES:
+	    ret = &ascii_casemap_matches;
+	    break;
+#ifdef ENABLE_REGEX
+	case B_REGEX:
+	    /* the ascii-casemap destinction is made during
+	       the compilation of the regex in verify_regex() */
+	    ret = &octet_regex;
+	    break;
+#endif
+	case B_VALUE:
+	    ret = lookup_rel(relation);
+	    *comprock = &strcasecmp;
+	    break;
+	}
+	break;/*end of ascii casemap */
+      case B_ASCIINUMERIC:
+	switch (mode) {
+	case B_IS:
+	    ret = &rel_eq;
+	    *comprock = (void **) &ascii_numeric_cmp;
+	    break;
+	case B_COUNT:
+	case B_VALUE:
+	    ret = lookup_rel(relation);
+	    *comprock = (void **) &ascii_numeric_cmp;
+	    break;
+	}
+	break;
+      }
+    return ret;
+}
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/sieve_err.h
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/sieve_err.h
@@ -0,0 +1,24 @@
+/*
+ * sieve_err.h:
+ * This file used to be automatically generated from sieve_er.et.
+ */
+struct et_list;
+
+#define SIEVE_FAIL                               (-1237848064L)
+#define SIEVE_NOT_FINALIZED                      (-1237848063L)
+#define SIEVE_PARSE_ERROR                        (-1237848062L)
+#define SIEVE_RUN_ERROR                          (-1237848061L)
+#define SIEVE_INTERNAL_ERROR                     (-1237848060L)
+#define SIEVE_NOMEM                              (-1237848059L)
+#define SIEVE_DONE                               (-1237848058L)
+extern const struct error_table et_siev_error_table;
+extern void initialize_siev_error_table(void);
+
+/* For compatibility with Heimdal */
+extern void initialize_siev_error_table_r(struct et_list **list);
+
+#define ERROR_TABLE_BASE_siev (-1237848064L)
+
+/* for compatibility with older versions... */
+#define init_siev_err_tbl initialize_siev_error_table
+#define siev_err_base ERROR_TABLE_BASE_siev
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/sieve_interface.h
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/sieve_interface.h
@@ -0,0 +1,172 @@
+/* sieve_interface.h -- interface for deliver
+ * $Id$
+ */
+/***********************************************************
+        Copyright 1999 by Carnegie Mellon University
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+******************************************************************/
+
+#ifndef SIEVE_H
+#define SIEVE_H
+
+#include <stdio.h>
+
+#define SIEVE_VERSION "CMU Sieve 2.2"
+
+/* error codes */
+#define SIEVE_OK (0)
+
+#include "sieve_err.h"
+
+/* external sieve types */
+typedef struct sieve_interp sieve_interp_t;
+typedef struct sieve_script sieve_script_t;
+typedef struct sieve_bytecode sieve_bytecode_t;
+typedef struct bytecode_info bytecode_info_t;
+
+typedef int sieve_callback(void *action_context, void *interp_context, 
+			   void *script_context,
+			   void *message_context, const char **errmsg);
+typedef int sieve_get_size(void *message_context, int *size);
+typedef int sieve_get_header(void *message_context, 
+			     const char *header,
+			     const char ***contents);
+typedef int sieve_get_envelope(void *message_context, 
+			       const char *field,
+			       const char ***contents);
+
+typedef struct sieve_vacation {
+    int min_response;		/* 0 -> defaults to 3 */
+    int max_response;		/* 0 -> defaults to 90 */
+
+    /* given a hash, say whether we've already responded to it in the last
+       days days.  return SIEVE_OK if we SHOULD autorespond (have not already)
+       or SIEVE_DONE if we SHOULD NOT. */
+    sieve_callback *autorespond;
+
+    /* mail the response */
+    sieve_callback *send_response;
+} sieve_vacation_t;
+
+typedef struct sieve_imapflags {
+    char **flag;		/* NULL -> defaults to \flagged */
+    int nflags;
+} sieve_imapflags_t;
+
+typedef struct sieve_redirect_context {
+    const char *addr;
+} sieve_redirect_context_t;
+
+typedef struct sieve_reject_context {
+    const char *msg;
+} sieve_reject_context_t;
+
+typedef struct sieve_fileinto_context {
+    const char *mailbox;
+    sieve_imapflags_t *imapflags;
+} sieve_fileinto_context_t;
+
+typedef struct sieve_keep_context {
+    sieve_imapflags_t *imapflags;
+} sieve_keep_context_t;
+
+typedef struct sieve_notify_context {
+    const char *method;
+    const char **options;
+    const char *priority;
+    const char *message;
+} sieve_notify_context_t;
+
+typedef struct sieve_autorespond_context {
+    unsigned char *hash;
+    int len;
+    int days;
+} sieve_autorespond_context_t;
+
+typedef struct sieve_send_response_context {
+    char *addr;
+    char *fromaddr;
+    const char *msg;
+    char *subj;
+    int mime;
+} sieve_send_response_context_t;
+
+/* build a sieve interpretor */
+int sieve_interp_alloc(sieve_interp_t **interp, void *interp_context);
+int sieve_interp_free(sieve_interp_t **interp);
+
+/* add the callbacks for actions. undefined behavior results if these
+   are called after sieve_script_parse is called! */
+int sieve_register_redirect(sieve_interp_t *interp, sieve_callback *f);
+int sieve_register_discard(sieve_interp_t *interp, sieve_callback *f);
+int sieve_register_reject(sieve_interp_t *interp, sieve_callback *f);
+int sieve_register_fileinto(sieve_interp_t *interp, sieve_callback *f);
+int sieve_register_keep(sieve_interp_t *interp, sieve_callback *f);
+int sieve_register_vacation(sieve_interp_t *interp, sieve_vacation_t *v);
+int sieve_register_imapflags(sieve_interp_t *interp, sieve_imapflags_t *mark);
+int sieve_register_notify(sieve_interp_t *interp, sieve_callback *f);
+
+/* add the callbacks for messages. again, undefined if used after
+   sieve_script_parse */
+int sieve_register_size(sieve_interp_t *interp, sieve_get_size *f);
+int sieve_register_header(sieve_interp_t *interp, sieve_get_header *f);
+int sieve_register_envelope(sieve_interp_t *interp, sieve_get_envelope *f);
+
+typedef int sieve_parse_error(int lineno, const char *msg, 
+			      void *interp_context,
+			      void *script_context);
+int sieve_register_parse_error(sieve_interp_t *interp, sieve_parse_error *f);
+
+typedef int sieve_execute_error(const char *msg, void *interp_context,
+				void *script_context, void *message_context);
+int sieve_register_execute_error(sieve_interp_t *interp, 
+				 sieve_execute_error *f);
+ 
+/* given an interpretor and a script, produce an executable script */
+int sieve_script_parse(sieve_interp_t *interp, FILE *script,
+		       void *script_context, sieve_script_t **ret);
+
+/* given a bytecode file descriptor, setup the sieve_bytecode_t */
+int sieve_script_load(const char *fname, sieve_bytecode_t **ret);
+
+/* Unload a sieve_bytecode_t */
+int sieve_script_unload(sieve_bytecode_t **s);
+
+/* Free a sieve_script_t */
+int sieve_script_free(sieve_script_t **s);
+
+/* execute bytecode on a message */
+int sieve_execute_bytecode(sieve_bytecode_t *script, sieve_interp_t *interp,
+			   void *script_context, void *message_context);
+
+/* Get space separated list of extensions supported by the implementation */
+const char *sieve_listextensions(void);
+
+/* Create a bytecode structure given a parsed commandlist */
+int sieve_generate_bytecode(bytecode_info_t **retval, sieve_script_t *s);
+
+/* Emit bytecode to a file descriptor */
+int sieve_emit_bytecode(int fd, bytecode_info_t *bc);
+
+/* Free a bytecode_info_t */
+void sieve_free_bytecode(bytecode_info_t **p);
+
+#endif
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/interp.c
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/interp.c
@@ -0,0 +1,203 @@
+/* interp.c -- sieve script interpretor builder
+ * Larry Greenfield
+ * $Id$
+ */
+/***********************************************************
+        Copyright 1999 by Carnegie Mellon University
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+******************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include "xmalloc.h"
+
+#include "sieve_interface.h"
+#include "interp.h"
+
+/* build a sieve interpretor */
+int sieve_interp_alloc(sieve_interp_t **interp, void *interp_context)
+{
+    sieve_interp_t *i;
+    static int initonce;
+
+    if (!initonce) {
+	initialize_siev_error_table();
+	initonce = 1;
+    }
+
+    *interp = NULL;
+    i = (sieve_interp_t *) xmalloc(sizeof(sieve_interp_t));
+    if (i == NULL) {
+	return SIEVE_NOMEM;
+    }
+
+    i->redirect = i->discard = i->reject = i->fileinto = i->keep = NULL;
+    i->getsize = NULL;
+    i->getheader = NULL;
+    i->getenvelope = NULL;
+    i->vacation = NULL;
+    i->notify = NULL;
+
+    i->markflags = NULL;
+
+    i->interp_context = interp_context;
+    i->err = NULL;
+
+    *interp = i;
+    return SIEVE_OK;
+}
+
+static const char *sieve_extensions = "fileinto reject envelope vacation"
+                                      " imapflags notify subaddress relational"
+                                      " comparator-i;ascii-numeric"
+#ifdef ENABLE_REGEX
+" regex";
+#else
+"";
+#endif /* ENABLE_REGEX */
+
+const char *sieve_listextensions(void)
+{
+    return sieve_extensions;
+}
+
+int sieve_interp_free(sieve_interp_t **interp)
+{
+    free(*interp);
+    
+    return SIEVE_OK;
+}
+
+/* add the callbacks */
+int sieve_register_redirect(sieve_interp_t *interp, sieve_callback *f)
+{
+    interp->redirect = f;
+
+    return SIEVE_OK;
+}
+
+int sieve_register_discard(sieve_interp_t *interp, sieve_callback *f)
+{
+    interp->discard = f;
+
+    return SIEVE_OK;
+}
+
+int sieve_register_reject(sieve_interp_t *interp, sieve_callback *f)
+{
+    interp->reject = f;
+
+    return SIEVE_OK;
+}
+
+int sieve_register_fileinto(sieve_interp_t *interp, sieve_callback *f)
+{
+    interp->fileinto = f;
+
+    return SIEVE_OK;
+}
+
+int sieve_register_keep(sieve_interp_t *interp, sieve_callback *f)
+{
+    interp->keep = f;
+ 
+    return SIEVE_OK;
+}
+
+static char *default_markflags[] = { "\\flagged" };
+static sieve_imapflags_t default_mark = { default_markflags, 1 };
+
+int sieve_register_imapflags(sieve_interp_t *interp, sieve_imapflags_t *mark)
+{
+    interp->markflags =
+	(mark && mark->flag && mark->nflags) ? mark : &default_mark;
+
+    return SIEVE_OK;
+}
+
+int sieve_register_notify(sieve_interp_t *interp, sieve_callback *f)
+{
+    interp->notify = f;
+ 
+    return SIEVE_OK;
+}
+
+/* add the callbacks for messages. again, undefined if used after
+   sieve_script_parse */
+int sieve_register_size(sieve_interp_t *interp, sieve_get_size *f)
+{
+    interp->getsize = f;
+    return SIEVE_OK;
+}
+
+int sieve_register_header(sieve_interp_t *interp, sieve_get_header *f)
+{
+    interp->getheader = f;
+    return SIEVE_OK;
+}
+
+int sieve_register_envelope(sieve_interp_t *interp, sieve_get_envelope *f)
+{
+    interp->getenvelope = f;
+    return SIEVE_OK;
+}
+
+int sieve_register_vacation(sieve_interp_t *interp, sieve_vacation_t *v)
+{
+    if (!interp->getenvelope) {
+	return SIEVE_NOT_FINALIZED; /* we need envelope for vacation! */
+    }
+
+    if (v->min_response == 0) v->min_response = 3;
+    if (v->max_response == 0) v->max_response = 90;
+    if (v->min_response < 0 || v->max_response < 7 || !v->autorespond
+	|| !v->send_response) {
+	return SIEVE_FAIL;
+    }
+
+    interp->vacation = v;
+    return SIEVE_OK;
+}
+
+int sieve_register_parse_error(sieve_interp_t *interp, sieve_parse_error *f)
+{
+    interp->err = f;
+    return SIEVE_OK;
+}
+
+int sieve_register_execute_error(sieve_interp_t *interp, sieve_execute_error *f)
+{
+    interp->execute_err = f;
+    return SIEVE_OK;
+}
+
+int interp_verify(sieve_interp_t *i)
+{
+    if (i->redirect && i->keep && i->getsize && i->getheader) {
+	return SIEVE_OK;
+    } else {
+	return SIEVE_NOT_FINALIZED;
+    }
+}
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/parseaddr.c
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/parseaddr.c
@@ -0,0 +1,370 @@
+/* parseaddr.c -- RFC 822 address parser
+ * $Id$
+ *
+ * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any other legal
+ *    details, please contact  
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "parseaddr.h"
+
+#define xmalloc malloc
+#define xstrdup strdup
+
+static char parseaddr_unspecified_domain[] = "unspecified-domain";
+
+static void parseaddr_append (struct address ***addrpp, char *name,
+				char *route, char *mailbox, char *domain,
+				char **freemep);
+static int parseaddr_phrase (char **inp, char **phrasep, char *specials);
+static int parseaddr_domain (char **inp, char **domainp, char **commmentp);
+static int parseaddr_route (char **inp, char **routep);
+
+/*
+ * Parse an address list in 's', appending address structures to
+ * the list pointed to by 'addrp'.
+ */
+void
+parseaddr_list(str, addrp)
+const char *str;
+struct address **addrp;
+{
+    char *s;
+    int ingroup = 0;
+    char *freeme;
+    int tok = ' ';
+    char *phrase, *route, *mailbox, *domain, *comment;
+
+    /* Skip down to the tail */
+    while (*addrp) {
+	addrp = &(*addrp)->next;
+    }
+
+    s = freeme = xstrdup(str);
+
+    while (tok) {
+	tok = parseaddr_phrase(&s, &phrase, ingroup ? ",@<;" : ",@<:");
+	switch (tok) {
+	case ',':
+	case '\0':
+	case ';':
+	    if (*phrase) {
+		parseaddr_append(&addrp, 0, 0, phrase, "", &freeme);
+	    }
+	    if (tok == ';') {
+		parseaddr_append(&addrp, 0, 0, 0, 0, &freeme);
+		ingroup = 0;
+	    }
+	    continue;
+
+	case ':':
+	    parseaddr_append(&addrp, 0, 0, phrase, 0, &freeme);
+	    ingroup++;
+	    continue;
+
+	case '@':
+	    tok = parseaddr_domain(&s, &domain, &comment);
+	    parseaddr_append(&addrp, comment, 0, phrase, domain, &freeme);
+	    continue;
+
+	case '<':
+	    tok = parseaddr_phrase(&s, &mailbox, "@>");
+	    if (tok == '@') {
+		route = 0;
+		if (!*mailbox) {
+		    *--s = '@';
+		    tok = parseaddr_route(&s, &route);
+		    if (tok != ':') {
+			parseaddr_append(&addrp, phrase, route, "", "", &freeme);
+			while (tok && tok != '>') tok = *s++;
+			continue;
+		    }
+		    tok = parseaddr_phrase(&s, &mailbox, "@>");
+		    if (tok != '@') {
+			parseaddr_append(&addrp, phrase, route, mailbox, "",
+					 &freeme);
+			continue;
+		    }
+		}
+		tok = parseaddr_domain(&s, &domain, 0);
+		parseaddr_append(&addrp, phrase, route, mailbox, domain,
+				 &freeme);
+		while (tok && tok != '>') tok = *s++;
+		continue; /* effectively auto-inserts a comma */
+	    }
+	    else {
+		parseaddr_append(&addrp, phrase, 0, mailbox, "", &freeme);
+	    }
+	}
+    }
+    if (ingroup) parseaddr_append(&addrp, 0, 0, 0, 0, &freeme);
+
+    if (freeme) free(freeme);
+}
+
+/*
+ * Free the address list 'addr'
+ */
+void
+parseaddr_free(addr)
+struct address *addr;
+{
+    struct address *next;
+
+    while (addr) {
+	if (addr->freeme) free(addr->freeme);
+	next = addr->next;
+	free((char *)addr);
+	addr = next;
+    }
+}
+
+/*
+ * Helper function to append a new address structure to and address list.
+ */
+static void
+parseaddr_append(addrpp, name, route, mailbox, domain, freemep)
+struct address ***addrpp;
+char *name;
+char *route;
+char *mailbox;
+char *domain;
+char **freemep;
+{
+    struct address *newaddr;
+
+    newaddr = (struct address *)xmalloc(sizeof(struct address));
+    if (name && *name) {
+	newaddr->name = name;
+    }
+    else {
+	newaddr->name = 0;
+    }
+
+    if (route && *route) {
+	newaddr->route = route;
+    }
+    else {
+	newaddr->route = 0;
+    }
+
+    newaddr->mailbox = mailbox;
+
+    if (domain && !*domain) {
+	domain = parseaddr_unspecified_domain;
+    }
+    newaddr->domain = domain;
+
+    newaddr->next = 0;
+    newaddr->freeme = *freemep;
+    *freemep = 0;
+
+    **addrpp = newaddr;
+    *addrpp = &newaddr->next;
+}
+
+/* Macro to skip white space and rfc822 comments */
+
+#define SKIPWHITESPACE(s) \
+{ \
+    int _c, _comment = 0; \
+ \
+    while ((_c = *(s))) { \
+	if (_c == '(') { \
+	    _comment = 1; \
+	    (s)++; \
+	    while ((_comment && (_c = *(s)))) { \
+		(s)++; \
+		if (_c == '\\' && *(s)) (s)++; \
+		else if (_c == '(') _comment++; \
+		else if (_c == ')') _comment--; \
+	    } \
+	    (s)--; \
+	} \
+	else if (!isspace(_c)) break; \
+	(s)++; \
+    } \
+}
+
+/*
+ * Parse an RFC 822 "phrase", stopping at 'specials'
+ */
+static int parseaddr_phrase(inp, phrasep, specials)
+char **inp;
+char **phrasep;
+char *specials;
+{
+    int c;
+    char *src = *inp;
+    char *dst;
+
+    SKIPWHITESPACE(src);
+
+    *phrasep = dst = src;
+
+    for (;;) {
+        c = *src++;
+	if (c == '\"') {
+	    while ((c = *src)) {
+		src++;
+		if (c == '\"') break;
+		if (c == '\\') {
+		    if (!(c = *src)) break;
+		    src++;
+		}
+		*dst++ = c;
+	    }
+	}
+	else if (isspace(c) || c == '(') {
+	    src--;
+	    SKIPWHITESPACE(src);
+	    *dst++ = ' ';
+	}
+	else if (!c || strchr(specials, c)) {
+	    if (dst > *phrasep && dst[-1] == ' ') dst--;
+	    *dst = '\0';
+	    *inp = src;
+	    return c;
+	}
+	else {
+	    *dst++ = c;
+	}
+    }
+}
+
+/*
+ * Parse a domain.  If 'commentp' is non-nil, parses any trailing comment
+ */
+static int parseaddr_domain(inp, domainp, commentp)
+char **inp;
+char **domainp;
+char **commentp;
+{
+    int c;
+    char *src = *inp;
+    char *dst;
+    char *cdst;
+    int comment;
+
+    if (commentp) *commentp = 0;
+    SKIPWHITESPACE(src);
+
+    *domainp = dst = src;
+
+    for (;;) {
+        c = *src++;
+	if (isalnum(c) || c == '-' || c == '[' || c == ']' || c == ':') {
+	    *dst++ = c;
+	    if (commentp) *commentp = 0;
+	}
+	else if (c == '.') {
+	    if (dst > *domainp && dst[-1] != '.') *dst++ = c;
+	    if (commentp) *commentp = 0;
+	}
+	else if (c == '(') {
+	    if (commentp) {
+		*commentp = cdst = src;
+		comment = 1;
+		while (comment && (c = *src)) {
+		    src++;
+		    if (c == '(') comment++;
+		    else if (c == ')') comment--;
+		    else if (c == '\\' && (c = *src)) src++;
+
+		    if (comment) *cdst++ = c;
+		}
+		*cdst = '\0';
+	    }
+	    else {
+		src--;
+		SKIPWHITESPACE(src);
+	    }
+	}
+	else if (!isspace(c)) {
+	    if (dst > *domainp && dst[-1] == '.') dst--;
+	    *dst = '\0';
+	    *inp = src;
+	    return c;
+	}
+    }
+}
+	
+/*
+ * Parse a source route (at-domain-list)
+ */
+static int parseaddr_route(inp, routep)
+char **inp;
+char **routep;
+{
+    int c;
+    char *src = *inp;
+    char *dst;
+
+    SKIPWHITESPACE(src);
+
+    *routep = dst = src;
+
+    for (;;) {
+        c = *src++;
+	if (isalnum(c) || c == '-' || c == '[' || c == ']' ||
+	    c == ',' || c == '@') {
+	    *dst++ = c;
+	}
+	else if (c == '.') {
+	    if (dst > *routep && dst[-1] != '.') *dst++ = c;
+	}
+	else if (isspace(c) || c == '(') {
+	    src--;
+	    SKIPWHITESPACE(src);
+	}
+	else {
+	    while (dst > *routep &&
+		   (dst[-1] == '.' || dst[-1] == ',' || dst[-1] == '@')) dst--;
+	    *dst = '\0';
+	    *inp = src;
+	    return c;
+	}
+    }
+}
+
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/sieve.y
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/sieve.y
@@ -0,0 +1,1090 @@
+%{
+/* sieve.y -- sieve parser
+ * Larry Greenfield
+ * $Id$
+ */
+/***********************************************************
+        Copyright 1999 by Carnegie Mellon University
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+******************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include "xmalloc.h"
+#include "comparator.h"
+#include "interp.h"
+#include "script.h"
+#include "tree.h"
+
+#include "imparse.h"
+#include "libconfig.h"
+
+    /* definitions */
+    extern int addrparse(void);
+
+struct vtags {
+    int days;
+    stringlist_t *addresses;
+    char *subject;
+    int mime;
+};
+
+struct htags {
+    char *comparator;
+    int comptag;
+    int relation;
+};
+
+struct aetags {
+    int addrtag;
+    char *comparator;
+    int comptag;
+    int relation;
+};
+
+struct ntags {
+    char *method;
+    char *id;
+    stringlist_t *options;
+    int priority;
+    char *message;
+};
+
+struct dtags {
+    int comptag;
+    int relation;
+    void *pattern;
+    int priority;
+};
+
+static commandlist_t *ret;
+static sieve_script_t *parse_script;
+static int check_reqs(stringlist_t *sl);
+static test_t *build_address(int t, struct aetags *ae,
+			     stringlist_t *sl, stringlist_t *pl);
+static test_t *build_header(int t, struct htags *h,
+			    stringlist_t *sl, stringlist_t *pl);
+static commandlist_t *build_vacation(int t, struct vtags *h, char *s);
+static commandlist_t *build_notify(int t, struct ntags *n);
+static commandlist_t *build_denotify(int t, struct dtags *n);
+static struct aetags *new_aetags(void);
+static struct aetags *canon_aetags(struct aetags *ae);
+static void free_aetags(struct aetags *ae);
+static struct htags *new_htags(void);
+static struct htags *canon_htags(struct htags *h);
+static void free_htags(struct htags *h);
+static struct vtags *new_vtags(void);
+static struct vtags *canon_vtags(struct vtags *v);
+static void free_vtags(struct vtags *v);
+static struct ntags *new_ntags(void);
+static struct ntags *canon_ntags(struct ntags *n);
+static void free_ntags(struct ntags *n);
+static struct dtags *new_dtags(void);
+static struct dtags *canon_dtags(struct dtags *d);
+static void free_dtags(struct dtags *d);
+
+static int verify_stringlist(stringlist_t *sl, int (*verify)(char *));
+static int verify_mailbox(char *s);
+static int verify_address(char *s);
+static int verify_header(char *s);
+static int verify_addrheader(char *s);
+static int verify_envelope(char *s);
+static int verify_flag(char *s);
+static int verify_relat(char *s);
+#ifdef ENABLE_REGEX
+static int verify_regex(char *s, int cflags);
+static int verify_regexs(stringlist_t *sl, char *comp);
+#endif
+static int verify_utf8(char *s);
+
+int yyerror(char *msg);
+extern int yylex(void);
+extern void yyrestart(FILE *f);
+
+#define YYERROR_VERBOSE /* i want better error messages! */
+%}
+
+%union {
+    int nval;
+    char *sval;
+    stringlist_t *sl;
+    test_t *test;
+    testlist_t *testl;
+    commandlist_t *cl;
+    struct vtags *vtag;
+    struct aetags *aetag;
+    struct htags *htag;
+    struct ntags *ntag;
+    struct dtags *dtag;
+}
+
+%token <nval> NUMBER
+%token <sval> STRING
+%token IF ELSIF ELSE
+%token REJCT FILEINTO REDIRECT KEEP STOP DISCARD VACATION REQUIRE
+%token SETFLAG ADDFLAG REMOVEFLAG MARK UNMARK
+%token NOTIFY DENOTIFY
+%token ANYOF ALLOF EXISTS SFALSE STRUE HEADER NOT SIZE ADDRESS ENVELOPE
+%token COMPARATOR IS CONTAINS MATCHES REGEX COUNT VALUE OVER UNDER
+%token GT GE LT LE EQ NE
+%token ALL LOCALPART DOMAIN USER DETAIL
+%token DAYS ADDRESSES SUBJECT MIME
+%token METHOD ID OPTIONS LOW NORMAL HIGH ANY MESSAGE
+
+%type <cl> commands command action elsif block
+%type <sl> stringlist strings
+%type <test> test
+%type <nval> comptag relcomp sizetag addrparttag addrorenv
+%type <testl> testlist tests
+%type <htag> htags
+%type <aetag> aetags
+%type <vtag> vtags
+%type <ntag> ntags
+%type <dtag> dtags
+%type <nval> priority
+
+%%
+
+start: reqs			{ ret = NULL; }
+	| reqs commands		{ ret = $2; }
+	;
+
+reqs: /* empty */
+	| require reqs
+	;
+
+require: REQUIRE stringlist ';'	{ if (!check_reqs($2)) {
+                                    yyerror("Unsupported features in require line");
+				    YYERROR; 
+                                  } }
+	;
+
+commands: command		{ $$ = $1; }
+	| command commands	{ $1->next = $2; $$ = $1; }
+	;
+
+command: action ';'		{ $$ = $1; }
+	| IF test block elsif   { $$ = new_if($2, $3, $4); }
+	| error ';'		{ $$ = new_command(STOP); }
+	;
+
+elsif: /* empty */               { $$ = NULL; }
+	| ELSIF test block elsif { $$ = new_if($2, $3, $4); }
+	| ELSE block             { $$ = $2; }
+	;
+
+action: REJCT STRING             { if (!parse_script->support.reject) {
+				     yyerror("reject require missing");
+				     YYERROR;
+				   }
+				   if (!verify_utf8($2)) {
+				     YYERROR; /* vu should call yyerror() */
+				   }
+				   $$ = new_command(REJCT);
+				   $$->u.str = $2; }
+	| FILEINTO STRING	 { if (!parse_script->support.fileinto) {
+				     yyerror("fileinto require missing");
+	                             YYERROR;
+                                   }
+				   if (!verify_mailbox($2)) {
+				     YYERROR; /* vm should call yyerror() */
+				   }
+	                           $$ = new_command(FILEINTO);
+				   $$->u.str = $2; }
+	| REDIRECT STRING         { $$ = new_command(REDIRECT);
+				   if (!verify_address($2)) {
+				     YYERROR; /* va should call yyerror() */
+				   }
+				   $$->u.str = $2; }
+	| KEEP			 { $$ = new_command(KEEP); }
+	| STOP			 { $$ = new_command(STOP); }
+	| DISCARD		 { $$ = new_command(DISCARD); }
+	| VACATION vtags STRING  { if (!parse_script->support.vacation) {
+				     yyerror("vacation require missing");
+				     YYERROR;
+				   }
+				   if (($2->mime == -1) && !verify_utf8($3)) {
+				     YYERROR; /* vu should call yyerror() */
+				   }
+  				   $$ = build_vacation(VACATION,
+					    canon_vtags($2), $3); }
+        | SETFLAG stringlist     { if (!parse_script->support.imapflags) {
+                                    yyerror("imapflags require missing");
+                                    YYERROR;
+                                   }
+                                  if (!verify_stringlist($2, verify_flag)) {
+                                    YYERROR; /* vf should call yyerror() */
+                                  }
+                                  $$ = new_command(SETFLAG);
+                                  $$->u.sl = $2; }
+         | ADDFLAG stringlist     { if (!parse_script->support.imapflags) {
+                                    yyerror("imapflags require missing");
+                                    YYERROR;
+                                    }
+                                  if (!verify_stringlist($2, verify_flag)) {
+                                    YYERROR; /* vf should call yyerror() */
+                                  }
+                                  $$ = new_command(ADDFLAG);
+                                  $$->u.sl = $2; }
+         | REMOVEFLAG stringlist  { if (!parse_script->support.imapflags) {
+                                    yyerror("imapflags require missing");
+                                    YYERROR;
+                                    }
+                                  if (!verify_stringlist($2, verify_flag)) {
+                                    YYERROR; /* vf should call yyerror() */
+                                  }
+                                  $$ = new_command(REMOVEFLAG);
+                                  $$->u.sl = $2; }
+         | MARK                   { if (!parse_script->support.imapflags) {
+                                    yyerror("imapflags require missing");
+                                    YYERROR;
+                                    }
+                                  $$ = new_command(MARK); }
+         | UNMARK                 { if (!parse_script->support.imapflags) {
+                                    yyerror("imapflags require missing");
+                                    YYERROR;
+                                    }
+                                  $$ = new_command(UNMARK); }
+
+         | NOTIFY ntags           { if (!parse_script->support.notify) {
+				       yyerror("notify require missing");
+				       $$ = new_command(NOTIFY); 
+				       YYERROR;
+	 			    } else {
+				      $$ = build_notify(NOTIFY,
+				             canon_ntags($2));
+				    } }
+         | DENOTIFY dtags         { if (!parse_script->support.notify) {
+                                       yyerror("notify require missing");
+				       $$ = new_command(DENOTIFY);
+				       YYERROR;
+				    } else {
+					$$ = build_denotify(DENOTIFY, canon_dtags($2));
+					if ($$ == NULL) { 
+			yyerror("unable to find a compatible comparator");
+			YYERROR; } } }
+	;
+
+ntags: /* empty */		 { $$ = new_ntags(); }
+	| ntags ID STRING	 { if ($$->id != NULL) { 
+					yyerror("duplicate :method"); YYERROR; }
+				   else { $$->id = $3; } }
+	| ntags METHOD STRING	 { if ($$->method != NULL) { 
+					yyerror("duplicate :method"); YYERROR; }
+				   else { $$->method = $3; } }
+	| ntags OPTIONS stringlist { if ($$->options != NULL) { 
+					yyerror("duplicate :options"); YYERROR; }
+				     else { $$->options = $3; } }
+        | ntags priority	 { if ($$->priority != -1) { 
+                                 yyerror("duplicate :priority"); YYERROR; }
+                                   else { $$->priority = $2; } }
+	| ntags MESSAGE STRING	 { if ($$->message != NULL) { 
+					yyerror("duplicate :message"); YYERROR; }
+				   else { $$->message = $3; } }
+	;
+
+dtags: /* empty */		 { $$ = new_dtags(); }
+	| dtags priority	 { if ($$->priority != -1) { 
+				yyerror("duplicate priority level"); YYERROR; }
+				   else { $$->priority = $2; } }
+	| dtags comptag STRING 	 { if ($$->comptag != -1)
+	                             { 
+					 yyerror("duplicate comparator type tag"); YYERROR;
+				     }
+	                           $$->comptag = $2;
+#ifdef ENABLE_REGEX
+				   if ($$->comptag == REGEX)
+				   {
+				       int cflags = REG_EXTENDED |
+					   REG_NOSUB | REG_ICASE;
+				       if (!verify_regex($3, cflags)) { YYERROR; }
+				   }
+#endif
+				   $$->pattern = $3;
+	                          }
+	| dtags relcomp STRING  { $$ = $1;
+				   if ($$->comptag != -1) { 
+			yyerror("duplicate comparator type tag"); YYERROR; }
+				   else { $$->comptag = $2;
+				   $$->relation = verify_relat($3);
+				   if ($$->relation==-1) 
+				     {YYERROR; /*vr called yyerror()*/ }
+				   } }
+	;
+
+priority: LOW                   { $$ = LOW; }
+        | NORMAL                { $$ = NORMAL; }
+        | HIGH                  { $$ = HIGH; }
+        ;
+
+vtags: /* empty */		 { $$ = new_vtags(); }
+	| vtags DAYS NUMBER	 { if ($$->days != -1) { 
+					yyerror("duplicate :days"); YYERROR; }
+				   else { $$->days = $3; } }
+	| vtags ADDRESSES stringlist { if ($$->addresses != NULL) { 
+					yyerror("duplicate :addresses"); 
+					YYERROR;
+				       } else if (!verify_stringlist($3,
+							verify_address)) {
+					  YYERROR;
+				       } else {
+					 $$->addresses = $3; } }
+	| vtags SUBJECT STRING	 { if ($$->subject != NULL) { 
+					yyerror("duplicate :subject"); 
+					YYERROR;
+				   } else if (!verify_utf8($3)) {
+				        YYERROR; /* vu should call yyerror() */
+				   } else { $$->subject = $3; } }
+	| vtags MIME		 { if ($$->mime != -1) { 
+					yyerror("duplicate :mime"); 
+					YYERROR; }
+				   else { $$->mime = MIME; } }
+	;
+
+stringlist: '[' strings ']'      { $$ = $2; }
+	| STRING		 { $$ = new_sl($1, NULL); }
+	;
+
+strings: STRING			 { $$ = new_sl($1, NULL); }
+	| STRING ',' strings	 { $$ = new_sl($1, $3); }
+	;
+
+block: '{' commands '}'		 { $$ = $2; }
+	| '{' '}'		 { $$ = NULL; }
+	;
+
+test:     ANYOF testlist	 { $$ = new_test(ANYOF); $$->u.tl = $2; }
+        | ALLOF testlist	 { $$ = new_test(ALLOF); $$->u.tl = $2; }
+        | EXISTS stringlist      { $$ = new_test(EXISTS); $$->u.sl = $2; }
+        | SFALSE		 { $$ = new_test(SFALSE); }
+	| STRUE			 { $$ = new_test(STRUE); }
+	| HEADER htags stringlist stringlist
+				 {
+				     if (!verify_stringlist($3, verify_header)) {
+					 YYERROR; /* vh should call yyerror() */
+				     }
+				     if (!verify_stringlist($4, verify_utf8)) {
+					 YYERROR; /* vu should call yyerror() */
+				     }
+				     
+				     $2 = canon_htags($2);
+#ifdef ENABLE_REGEX
+				     if ($2->comptag == REGEX)
+				     {
+					 if (!(verify_regexs($4, $2->comparator)))
+					 { YYERROR; }
+				     }
+#endif
+				     $$ = build_header(HEADER, $2, $3, $4);
+				     if ($$ == NULL) { 
+					 yyerror("unable to find a compatible comparator");
+					 YYERROR; } 
+				 }
+
+
+        | addrorenv aetags stringlist stringlist
+				 { 
+				     if (($1 == ADDRESS) &&
+					 !verify_stringlist($3, verify_addrheader))
+					 { YYERROR; }
+				     else if (($1 == ENVELOPE) &&
+					      !verify_stringlist($3, verify_envelope))
+					 { YYERROR; }
+				     $2 = canon_aetags($2);
+#ifdef ENABLE_REGEX
+				     if ($2->comptag == REGEX)
+				     {
+					 if (!( verify_regexs($4, $2->comparator)))
+					 { YYERROR; }
+				     }
+#endif
+				     $$ = build_address($1, $2, $3, $4);
+				     if ($$ == NULL) { 
+					 yyerror("unable to find a compatible comparator");
+					 YYERROR; } 
+				 }
+
+	| NOT test		 { $$ = new_test(NOT); $$->u.t = $2; }
+	| SIZE sizetag NUMBER    { $$ = new_test(SIZE); $$->u.sz.t = $2;
+		                   $$->u.sz.n = $3; }
+	| error			 { $$ = NULL; }
+	;
+
+addrorenv: ADDRESS		 { $$ = ADDRESS; }
+	| ENVELOPE		 {if (!parse_script->support.envelope)
+	                              {yyerror("envelope require missing"); YYERROR;}
+	                          else{$$ = ENVELOPE; }
+	                         }
+
+	;
+
+aetags: /* empty */              { $$ = new_aetags(); }
+        | aetags addrparttag	 { $$ = $1;
+				   if ($$->addrtag != -1) { 
+			yyerror("duplicate or conflicting address part tag");
+			YYERROR; }
+				   else { $$->addrtag = $2; } }
+	| aetags comptag         { $$ = $1;
+				   if ($$->comptag != -1) { 
+			yyerror("duplicate comparator type tag"); YYERROR; }
+				   else { $$->comptag = $2; } }
+	| aetags relcomp STRING{ $$ = $1;
+				   if ($$->comptag != -1) { 
+			yyerror("duplicate comparator type tag"); YYERROR; }
+				   else { $$->comptag = $2;
+				   $$->relation = verify_relat($3);
+				   if ($$->relation==-1) 
+				     {YYERROR; /*vr called yyerror()*/ }
+				   } }
+        | aetags COMPARATOR STRING { $$ = $1;
+	if ($$->comparator != NULL) { 
+			yyerror("duplicate comparator tag"); YYERROR; }
+				   else if (!strcmp($3, "i;ascii-numeric") &&
+					    !parse_script->support.i_ascii_numeric) {
+			yyerror("comparator-i;ascii-numeric require missing");
+			YYERROR; }
+				   else { $$->comparator = $3; } }
+	;
+
+htags: /* empty */		 { $$ = new_htags(); }
+	| htags comptag		 { $$ = $1;
+				   if ($$->comptag != -1) { 
+			yyerror("duplicate comparator type tag"); YYERROR; }
+				   else { $$->comptag = $2; } }
+	| htags relcomp STRING { $$ = $1;
+				   if ($$->comptag != -1) { 
+			yyerror("duplicate comparator type tag"); YYERROR; }
+				   else { $$->comptag = $2;
+				   $$->relation = verify_relat($3);
+				   if ($$->relation==-1) 
+				     {YYERROR; /*vr called yyerror()*/ }
+				   } }
+	| htags COMPARATOR STRING { $$ = $1;
+				   if ($$->comparator != NULL) { 
+			 yyerror("duplicate comparator tag"); YYERROR; }
+				   else if (!strcmp($3, "i;ascii-numeric") &&
+					    !parse_script->support.i_ascii_numeric) { 
+			 yyerror("comparator-i;ascii-numeric require missing");  YYERROR; }
+				   else { 
+				     $$->comparator = $3; } }
+        ;
+
+
+addrparttag: ALL                 { $$ = ALL; }
+	| LOCALPART		 { $$ = LOCALPART; }
+	| DOMAIN                 { $$ = DOMAIN; }
+	| USER                   { if (!parse_script->support.subaddress) {
+				     yyerror("subaddress require missing");
+				     YYERROR;
+				   }
+				   $$ = USER; }  
+	| DETAIL                { if (!parse_script->support.subaddress) {
+				     yyerror("subaddress require missing");
+				     YYERROR;
+				   }
+				   $$ = DETAIL; }
+	;
+comptag: IS			 { $$ = IS; }
+	| CONTAINS		 { $$ = CONTAINS; }
+	| MATCHES		 { $$ = MATCHES; }
+	| REGEX			 { if (!parse_script->support.regex) {
+				     yyerror("regex require missing");
+				     YYERROR;
+				   }
+				   $$ = REGEX; }
+	;
+
+relcomp: COUNT			 { if (!parse_script->support.relational) {
+				     yyerror("relational require missing");
+				     YYERROR;
+				   }
+				   $$ = COUNT; }
+	| VALUE			 { if (!parse_script->support.relational) {
+				     yyerror("relational require missing");
+				     YYERROR;
+				   }
+				   $$ = VALUE; }
+	;
+
+
+sizetag: OVER			 { $$ = OVER; }
+	| UNDER			 { $$ = UNDER; }
+	;
+
+testlist: '(' tests ')'		 { $$ = $2; }
+	;
+
+tests: test                      { $$ = new_testlist($1, NULL); }
+	| test ',' tests         { $$ = new_testlist($1, $3); }
+	;
+
+%%
+commandlist_t *sieve_parse(sieve_script_t *script, FILE *f)
+{
+    commandlist_t *t;
+
+    parse_script = script;
+    yyrestart(f);
+    if (yyparse()) {
+	t = NULL;
+    } else {
+	t = ret;
+    }
+    ret = NULL;
+    return t;
+}
+
+int yyerror(char *msg)
+{
+    extern int yylineno;
+    int ret;
+
+    parse_script->err++;
+    if (parse_script->interp.err) {
+	ret = parse_script->interp.err(yylineno, msg, 
+				       parse_script->interp.interp_context,
+				       parse_script->script_context);
+    }
+
+    return 0;
+}
+
+static int check_reqs(stringlist_t *sl)
+{
+    int i = 1;
+    stringlist_t *s;
+    
+    while (sl != NULL) {
+	s = sl;
+	sl = sl->next;
+
+	i &= script_require(parse_script, s->s);
+
+	if (s->s) free(s->s);
+	free(s);
+    }
+    return i;
+}
+
+static test_t *build_address(int t, struct aetags *ae,
+			     stringlist_t *sl, stringlist_t *pl)
+{
+    test_t *ret = new_test(t);	/* can be either ADDRESS or ENVELOPE */
+
+    assert((t == ADDRESS) || (t == ENVELOPE));
+
+    if (ret) {
+	ret->u.ae.comptag = ae->comptag;
+	ret->u.ae.relation=ae->relation;
+	ret->u.ae.comparator=strdup(ae->comparator);
+	ret->u.ae.sl = sl;
+	ret->u.ae.pl = pl;
+	ret->u.ae.addrpart = ae->addrtag;
+	free_aetags(ae);
+
+    }
+    return ret;
+}
+
+static test_t *build_header(int t, struct htags *h,
+			    stringlist_t *sl, stringlist_t *pl)
+{
+    test_t *ret = new_test(t);	/* can be HEADER */
+
+    assert(t == HEADER);
+
+    if (ret) {
+	ret->u.h.comptag = h->comptag;
+	ret->u.h.relation=h->relation;
+	ret->u.h.comparator=strdup(h->comparator);
+	ret->u.h.sl = sl;
+	ret->u.h.pl = pl;
+	free_htags(h);
+    }
+    return ret;
+}
+
+static commandlist_t *build_vacation(int t, struct vtags *v, char *reason)
+{
+    commandlist_t *ret = new_command(t);
+
+    assert(t == VACATION);
+
+    if (ret) {
+	ret->u.v.subject = v->subject; v->subject = NULL;
+	ret->u.v.days = v->days;
+	ret->u.v.mime = v->mime;
+	ret->u.v.addresses = v->addresses; v->addresses = NULL;
+	free_vtags(v);
+	ret->u.v.message = reason;
+    }
+    return ret;
+}
+
+static commandlist_t *build_notify(int t, struct ntags *n)
+{
+    commandlist_t *ret = new_command(t);
+
+    assert(t == NOTIFY);
+       if (ret) {
+	ret->u.n.method = n->method; n->method = NULL;
+	ret->u.n.id = n->id; n->id = NULL;
+	ret->u.n.options = n->options; n->options = NULL;
+	ret->u.n.priority = n->priority;
+	ret->u.n.message = n->message; n->message = NULL;
+	free_ntags(n);
+    }
+    return ret;
+}
+
+static commandlist_t *build_denotify(int t, struct dtags *d)
+{
+    commandlist_t *ret = new_command(t);
+
+    assert(t == DENOTIFY);
+
+    if (ret) {
+	ret->u.d.comptag = d->comptag;
+	ret->u.d.relation=d->relation;
+	ret->u.d.pattern = d->pattern; d->pattern = NULL;
+	ret->u.d.priority = d->priority;
+	free_dtags(d);
+    }
+    return ret;
+}
+
+static struct aetags *new_aetags(void)
+{
+    struct aetags *r = (struct aetags *) xmalloc(sizeof(struct aetags));
+
+    r->addrtag = r->comptag = r->relation=-1;
+    r->comparator=NULL;
+
+    return r;
+}
+
+static struct aetags *canon_aetags(struct aetags *ae)
+{
+    if (ae->addrtag == -1) { ae->addrtag = ALL; }
+    if (ae->comparator == NULL) {
+        ae->comparator = xstrdup("i;ascii-casemap");
+    }
+    if (ae->comptag == -1) { ae->comptag = IS; }
+    return ae;
+}
+
+static void free_aetags(struct aetags *ae)
+{
+    free(ae->comparator);
+     free(ae);
+}
+
+static struct htags *new_htags(void)
+{
+    struct htags *r = (struct htags *) xmalloc(sizeof(struct htags));
+
+    r->comptag = r->relation= -1;
+    
+    r->comparator = NULL;
+
+    return r;
+}
+
+static struct htags *canon_htags(struct htags *h)
+{
+    if (h->comparator == NULL) {
+	h->comparator = xstrdup("i;ascii-casemap");
+    }
+    if (h->comptag == -1) { h->comptag = IS; }
+    return h;
+}
+
+static void free_htags(struct htags *h)
+{
+    free(h->comparator);
+    free(h);
+}
+
+static struct vtags *new_vtags(void)
+{
+    struct vtags *r = (struct vtags *) xmalloc(sizeof(struct vtags));
+
+    r->days = -1;
+    r->addresses = NULL;
+    r->subject = NULL;
+    r->mime = -1;
+
+    return r;
+}
+
+static struct vtags *canon_vtags(struct vtags *v)
+{
+    assert(parse_script->interp.vacation != NULL);
+
+    if (v->days == -1) { v->days = 7; }
+    if (v->days < parse_script->interp.vacation->min_response) 
+       { v->days = parse_script->interp.vacation->min_response; }
+    if (v->days > parse_script->interp.vacation->max_response)
+       { v->days = parse_script->interp.vacation->max_response; }
+    if (v->mime == -1) { v->mime = 0; }
+
+    return v;
+}
+
+static void free_vtags(struct vtags *v)
+{
+    if (v->addresses) { free_sl(v->addresses); }
+    if (v->subject) { free(v->subject); }
+    free(v);
+}
+
+static struct ntags *new_ntags(void)
+{
+    struct ntags *r = (struct ntags *) xmalloc(sizeof(struct ntags));
+
+    r->method = NULL;
+    r->id = NULL;
+    r->options = NULL;
+    r->priority = -1;
+    r->message = NULL;
+
+    return r;
+}
+
+static struct ntags *canon_ntags(struct ntags *n)
+{
+    if (n->priority == -1) { n->priority = NORMAL; }
+    if (n->message == NULL) { n->message = strdup("$from$: $subject$"); }
+    if (n->method == NULL) { n->method = strdup("default"); }
+    return n;
+}
+static struct dtags *canon_dtags(struct dtags *d)
+{
+    if (d->priority == -1) { d->priority = ANY; }
+    if (d->comptag == -1) { d->comptag = ANY; }
+       return d;
+}
+
+static void free_ntags(struct ntags *n)
+{
+    if (n->method) { free(n->method); }
+    if (n->id) { free(n->id); }
+    if (n->options) { free_sl(n->options); }
+    if (n->message) { free(n->message); }
+    free(n);
+}
+
+static struct dtags *new_dtags(void)
+{
+    struct dtags *r = (struct dtags *) xmalloc(sizeof(struct dtags));
+
+    r->comptag = r->priority= r->relation = -1;
+    r->pattern  = NULL;
+
+    return r;
+}
+
+static void free_dtags(struct dtags *d)
+{
+    if (d->pattern) free(d->pattern);
+    free(d);
+}
+
+static int verify_stringlist(stringlist_t *sl, int (*verify)(char *))
+{
+    for (; sl != NULL && verify(sl->s); sl = sl->next) ;
+    return (sl == NULL);
+}
+
+char *addrptr;		/* pointer to address string for address lexer */
+char addrerr[500];	/* buffer for address parser error messages */
+
+static int verify_address(char *s)
+{
+    char errbuf[500];
+
+    addrptr = s;
+    addrerr[0] = '\0';	/* paranoia */
+    if (addrparse()) {
+	snprintf(errbuf, sizeof(errbuf), "address '%s': %s", s, addrerr);
+	yyerror(errbuf);
+	return 0;
+    }
+    return 1;
+}
+
+static int verify_mailbox(char *s)
+{
+    if (!verify_utf8(s)) return 0;
+
+    /* xxx if not a mailbox, call yyerror */
+    return 1;
+}
+
+static int verify_header(char *hdr)
+{
+    char *h = hdr;
+    char errbuf[100];
+
+    while (*h) {
+	/* field-name      =       1*ftext
+	   ftext           =       %d33-57 / %d59-126         
+	   ; Any character except
+	   ;  controls, SP, and
+	   ;  ":". */
+	if (!((*h >= 33 && *h <= 57) || (*h >= 59 && *h <= 126))) {
+	    snprintf(errbuf, sizeof(errbuf),
+		     "header '%s': not a valid header", hdr);
+	    yyerror(errbuf);
+	    return 0;
+	}
+	h++;
+    }
+    return 1;
+}
+ 
+static int verify_addrheader(char *hdr)
+{
+    const char **h, *hdrs[] = {
+	"from", "sender", "reply-to",	/* RFC2822 originator fields */
+	"to", "cc", "bcc",		/* RFC2822 destination fields */
+	"resent-from", "resent-sender",	/* RFC2822 resent fields */
+	"resent-to", "resent-cc", "resent-bcc",
+	"return-path",			/* RFC2822 trace fields */
+	"disposition-notification-to",	/* RFC2298 MDN request fields */
+	"delivered-to",			/* non-standard (loop detection) */
+	"approved",			/* RFC1036 moderator/control fields */
+	NULL
+    };
+    char errbuf[100];
+
+    if (!config_getswitch(IMAPOPT_RFC3028_STRICT))
+	return verify_header(hdr);
+
+    for (lcase(hdr), h = hdrs; *h; h++) {
+	if (!strcmp(*h, hdr)) return 1;
+    }
+
+    snprintf(errbuf, sizeof(errbuf),
+	     "header '%s': not a valid header for an address test", hdr);
+    yyerror(errbuf);
+    return 0;
+}
+ 
+static int verify_envelope(char *env)
+{
+    char errbuf[100];
+
+    lcase(env);
+    if (!config_getswitch(IMAPOPT_RFC3028_STRICT) ||
+	!strcmp(env, "from") || !strcmp(env, "to") || !strcmp(env, "auth")) {
+	return 1;
+    }
+
+    snprintf(errbuf, sizeof(errbuf),
+	     "env-part '%s': not a valid part for an envelope test", env);
+    yyerror(errbuf);
+    return 0;
+}
+ 
+static int verify_relat(char *r)
+{/* this really should have been a token to begin with.*/
+    char errbuf[100];
+	lcase(r);
+	if (!strcmp(r, "gt")) {return GT;}
+	else if (!strcmp(r, "ge")) {return GE;}
+	else if (!strcmp(r, "lt")) {return LT;}
+	else if (!strcmp(r, "le")) {return LE;}
+	else if (!strcmp(r, "ne")) {return NE;}
+	else if (!strcmp(r, "eq")) {return EQ;}
+	else{
+	  sprintf(errbuf, "flag '%s': not a valid relational operation", r);
+	  yyerror(errbuf);
+	  return -1;
+	}
+	
+}
+
+
+
+
+static int verify_flag(char *f)
+{
+    char errbuf[100];
+ 
+    if (f[0] == '\\') {
+	lcase(f);
+	if (strcmp(f, "\\seen") && strcmp(f, "\\answered") &&
+	    strcmp(f, "\\flagged") && strcmp(f, "\\draft") &&
+	    strcmp(f, "\\deleted")) {
+	    snprintf(errbuf, sizeof(errbuf),
+		     "flag '%s': not a system flag", f);
+	    yyerror(errbuf);
+	    return 0;
+	}
+	return 1;
+    }
+    if (!imparse_isatom(f)) {
+	snprintf(errbuf, sizeof(errbuf), "flag '%s': not a valid keyword", f);
+	yyerror(errbuf);
+	return 0;
+    }
+    return 1;
+}
+ 
+#ifdef ENABLE_REGEX
+static int verify_regex(char *s, int cflags)
+{
+    int ret;
+    char errbuf[100];
+    regex_t *reg = (regex_t *) xmalloc(sizeof(regex_t));
+
+     if ((ret = regcomp(reg, s, cflags)) != 0) {
+	(void) regerror(ret, reg, errbuf, sizeof(errbuf));
+	yyerror(errbuf);
+	free(reg);
+	return 0;
+	}
+    free(reg);
+    return 1;
+}
+
+static int verify_regexs(stringlist_t *sl, char *comp)
+{
+    stringlist_t *sl2;
+    int cflags = REG_EXTENDED | REG_NOSUB;
+ 
+
+    if (!strcmp(comp, "i;ascii-casemap")) {
+	cflags |= REG_ICASE;
+    }
+
+    for (sl2 = sl; sl2 != NULL; sl2 = sl2->next) {
+	if ((verify_regex(sl2->s, cflags)) == 0) {
+	    break;
+	}
+    }
+    if (sl2 == NULL) {
+	return 1;
+    }
+    return 0;
+}
+#endif
+
+/*
+ * Valid UTF-8 check (from RFC 2640 Annex B.1)
+ *
+ * The following routine checks if a byte sequence is valid UTF-8. This
+ * is done by checking for the proper tagging of the first and following
+ * bytes to make sure they conform to the UTF-8 format. It then checks
+ * to assure that the data part of the UTF-8 sequence conforms to the
+ * proper range allowed by the encoding. Note: This routine will not
+ * detect characters that have not been assigned and therefore do not
+ * exist.
+ */
+static int verify_utf8(char *s)
+{
+    const unsigned char *buf = (const unsigned char *)s;
+    const unsigned char *endbuf = buf + strlen(s);
+    unsigned char byte2mask = 0x00, c;
+    int trailing = 0;  /* trailing (continuation) bytes to follow */
+
+    while (buf != endbuf) {
+	c = *buf++;
+	if (trailing) {
+	    if ((c & 0xC0) == 0x80) {		/* Does trailing byte
+						   follow UTF-8 format? */
+		if (byte2mask) {		/* Need to check 2nd byte
+						   for proper range? */
+		    if (c & byte2mask)		/* Are appropriate bits set? */
+			byte2mask = 0x00;
+		    else
+			break;
+		}
+		trailing--;
+	    }
+	    else
+		break;
+	}
+	else {
+	    if ((c & 0x80) == 0x00)		/* valid 1 byte UTF-8 */
+		continue;
+	    else if ((c & 0xE0) == 0xC0)	/* valid 2 byte UTF-8 */
+		if (c & 0x1E) {			/* Is UTF-8 byte
+						   in proper range? */
+		    trailing = 1;
+		}
+		else
+		    break;
+	    else if ((c & 0xF0) == 0xE0) {	/* valid 3 byte UTF-8 */
+		if (!(c & 0x0F)) {		/* Is UTF-8 byte
+						   in proper range? */
+		    byte2mask = 0x20;		/* If not, set mask
+						   to check next byte */
+		}
+		trailing = 2;
+	    }
+	    else if ((c & 0xF8) == 0xF0) {	/* valid 4 byte UTF-8 */
+		if (!(c & 0x07)) {		/* Is UTF-8 byte
+						   in proper range? */
+		    byte2mask = 0x30;		/* If not, set mask
+						   to check next byte */
+		}
+		trailing = 3;
+	    }
+	    else if ((c & 0xFC) == 0xF8) {	/* valid 5 byte UTF-8 */
+		if (!(c & 0x03)) {		/* Is UTF-8 byte
+						   in proper range? */
+		    byte2mask = 0x38;		/* If not, set mask
+						   to check next byte */
+		}
+		trailing = 4;
+	    }
+	    else if ((c & 0xFE) == 0xFC) {	/* valid 6 byte UTF-8 */
+		if (!(c & 0x01)) {		/* Is UTF-8 byte
+						   in proper range? */
+		    byte2mask = 0x3C;		/* If not, set mask
+						   to check next byte */
+		}
+		trailing = 5;
+	    }
+	    else
+		break;
+	}
+    }
+
+    if ((buf != endbuf) || trailing) {
+	char errbuf[100];
+
+	snprintf(errbuf, sizeof(errbuf),
+		 "string '%s': not valid utf8", s);
+	yyerror(errbuf);
+	return 0;
+    }
+
+    return 1;
+}
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/COPYING
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/COPYING
@@ -0,0 +1,26 @@
+/* cmu-sieve
+ * Larry Greenfield
+ * $Id$
+ */
+/***********************************************************
+        Copyright 1998 by Carnegie Mellon University
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+******************************************************************/
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/bc_generate.c
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/bc_generate.c
@@ -0,0 +1,708 @@
+/* bc_generate.c -- sieve bytecode- almost flattened bytecode
+ * Rob Siemborski
+ * $Id$
+ */
+/***********************************************************
+        Copyright 2001 by Carnegie Mellon University
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+******************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "xmalloc.h"
+#include "sieve_interface.h"
+
+#include "script.h"
+#include "tree.h"
+#include "sieve.h"
+
+#include "bytecode.h"
+
+#include <assert.h>
+#include <string.h>
+
+
+
+struct bytecode_info 
+{
+    bytecode_t *data;/* pointer to almost-flat bytecode */
+    size_t scriptend; /* used by emit code to know final length of bytecode */
+    size_t reallen; /* allocated length of 'data' */
+};
+
+static int bc_test_generate(int codep, bytecode_info_t *retval, test_t *t);
+
+/* returns false if the request can't be satisfied, true if it can. */
+
+static int atleast(bytecode_info_t *arr, size_t len) 
+{
+    if(arr->reallen < len) {
+	/* too small; double if that's big enough, otherwise increase to the
+	   requested size. */
+	arr->reallen = (len > arr->reallen * 2 ? len : arr->reallen * 2);
+	arr->data = xrealloc(arr->data, arr->reallen*sizeof(bytecode_t));
+	if(!arr->data) 
+	{ /* out of memory? */
+	    return 0;
+	}
+    }
+    
+    return 1;
+}
+
+/*
+ * functions of the form bc_XXX_generate have the following properties:
+ * on success they return an int that corresponds to the next empty location
+ * for code, and on failure they return -1.
+ *
+ *  they will take a  bytecode_info_t as a parameter and modify it by
+ *  making it larger and adding more bytecommands in the pass 1 form
+ */
+
+/* given a location and a string list, compile it into almost-flat form.
+ * <list len> <string len><string ptr><string len><string ptr> etc... */
+static int bc_stringlist_generate(int codep, bytecode_info_t *retval,
+				  stringlist_t *sl) 
+{
+    int len_codep = codep;
+    int strcount = 0;
+    stringlist_t *cur;
+    
+    codep++;
+
+    /* Bounds check the string list length */
+    if(!atleast(retval,codep+1)) 
+	return -1;
+
+    for(cur=sl; cur; cur=cur->next) 
+    {
+	strcount++;
+	assert((cur->s)!=NULL);
+	
+	/* Bounds check for each string before we allocate it */
+	if(!atleast(retval,codep+2)) 
+	    return -1;
+
+	retval->data[codep++].len = strlen(cur->s);
+	retval->data[codep++].str = cur->s;
+    }
+    
+    retval->data[len_codep].listlen = strcount;
+    return codep;
+}
+
+
+/* write a list of tests into almost-flat form, starting at codep.
+ * returns the next code location, -1 on error. */
+
+/* <list len> <next test ptr> <test ...> <next test ptr> <test ...> ... */
+static int bc_testlist_generate(int codep, bytecode_info_t *retval, 
+				testlist_t *tl) 
+{
+    int len_codep = codep;
+    int testcount = 0;
+    testlist_t *cur;
+    
+    codep++;
+
+    /* Bounds check the test list length */
+    if(!atleast(retval,codep+1)) 
+	return -1;
+       
+    for(cur=tl; cur; cur=cur->next) {
+	int oldcodep = codep;
+
+	/* Make room for tail marker */
+	if(!atleast(retval,codep+1)) 
+	    return -1;
+
+	testcount++;
+	codep = bc_test_generate(codep+1, retval, cur->t);
+
+	retval->data[oldcodep].jump = codep;
+    }
+
+    retval->data[len_codep].listlen = testcount;
+        
+    return codep;
+}
+/* output a relation into almost-flat form at codep.
+ * returns new codep on success, -1 on failure. */
+static int bc_relation_generate(int codep, bytecode_info_t *retval, int relat)
+{
+    if (!atleast(retval, codep + 1)) return -1;
+    switch (relat)
+    {
+    case GT:
+	retval->data[codep++].value= B_GT;
+	break;
+    case GE:
+	retval->data[codep++].value= B_GE;
+	break; 
+    case LT:
+	retval->data[codep++].value= B_LT;
+	break;
+    case LE:
+	retval->data[codep++].value= B_LE;
+	break;
+    case EQ:
+	retval->data[codep++].value= B_EQ;
+	break;
+    case NE:
+	retval->data[codep++].value= B_NE;
+	break;
+    default:
+	/* comparator has no relational field */
+	retval->data[codep++].value=  -1;
+	break;
+    }
+    return codep;
+}
+/* writes a single comparator into almost-flat form starting at codep.
+ * will always write out 3 words
+ * returns the next code location or -1 on error. */
+static int bc_comparator_generate(int codep, bytecode_info_t *retval,
+                                  int comptag, int relat,
+                                  const char *comparator)
+{
+    assert(retval != NULL);
+
+    /* comptag */
+    if (!atleast(retval, codep + 1)) return -1;
+
+    switch (comptag) {
+    case IS:
+        retval->data[codep++].value = B_IS;
+        break;
+    case CONTAINS:
+        retval->data[codep++].value = B_CONTAINS;
+        break;
+    case MATCHES:
+        retval->data[codep++].value = B_MATCHES;
+        break;
+#ifdef ENABLE_REGEX
+    case REGEX:
+        retval->data[codep++].value = B_REGEX;
+        break;
+#endif
+    case COUNT:
+        retval->data[codep++].value = B_COUNT;
+        break;
+    case VALUE:
+        retval->data[codep++].value = B_VALUE;
+        break;
+
+    default:
+        return -1;
+    }
+  
+    /*relation*/
+    codep = bc_relation_generate(codep, retval, relat);
+  
+    /* comparator (value specified with :comparator) */
+    if (!atleast(retval, codep + 1)) return -1;
+  
+    /* xxx perhaps extend comparator.h to have
+       lookup_comp return an index, and then
+       lookup_by_index return the actual comparator?
+       
+       we can then eliminate the comptag above, too. */
+    
+    if (!strcmp (comparator, "i;octet"))
+        retval->data[codep++].value = B_OCTET;
+    else if (!strcmp (comparator, "i;ascii-casemap"))
+        retval->data[codep++].value = B_ASCIICASEMAP;
+    else if (!strcmp (comparator, "i;ascii-numeric"))
+        retval->data[codep++].value = B_ASCIINUMERIC;
+
+    return codep;
+}
+
+
+
+/* writes a single test into almost-flat form starting at codep.
+ * returns the next code location or -1 on error. */
+static int bc_test_generate(int codep, bytecode_info_t *retval, test_t *t)
+{
+    if(!retval) return -1;
+    switch(t->type) {
+    case STRUE: /* BC_TRUE */
+	if(!atleast(retval,codep+1)) return -1;
+	retval->data[codep++].op = BC_TRUE;
+	break;
+    case SFALSE:/* BC_FALSE */
+	if(!atleast(retval,codep+1)) return -1;
+	retval->data[codep++].op = BC_FALSE;
+	break;
+    case NOT: /* BC_NOT {subtest : test} */
+	if(!atleast(retval,codep+1)) return -1;
+	retval->data[codep++].op = BC_NOT;
+	codep = bc_test_generate(codep, retval, t->u.t);
+	if (codep == -1) return -1;
+	break;
+    case SIZE: /* BC_SIZE (B_OVER | B_UNDER) {size : int} */
+	if(!atleast(retval,codep+3)) return -1;
+	retval->data[codep++].op = BC_SIZE;
+	retval->data[codep++].value = (t->u.sz.t == OVER
+				       ? B_OVER : B_UNDER);
+	retval->data[codep++].value = t->u.sz.n;
+	break;
+    case EXISTS:/* BC_EXISTS { headers : string list } */
+	if(!atleast(retval,codep+1)) return -1;
+	retval->data[codep++].op = BC_EXISTS;
+	codep= bc_stringlist_generate(codep, retval, t->u.sl);
+	break;
+    case ANYOF:/* BC_ANYOF { tests : test list } */
+	if(!atleast(retval,codep+1)) return -1;
+	retval->data[codep++].op = BC_ANYOF;
+	codep=bc_testlist_generate(codep, retval, t->u.tl);
+	if (codep == -1) return -1;
+	break;
+    case ALLOF: /* BC_ALLOF { tests : test list } */
+	if(!atleast(retval,codep+1)) return -1;
+	retval->data[codep++].op = BC_ALLOF;
+	codep= bc_testlist_generate(codep, retval, t->u.tl);
+	if (codep == -1) return -1;
+	break;
+    case HEADER:
+	/* BC_HEADER { c: comparator } { headers : string list }
+	   { patterns : string list } 
+	*/
+      
+	if(!atleast(retval,codep + 1)) return -1;
+	retval->data[codep++].op = BC_HEADER;
+      
+	/* comparator */
+	codep = bc_comparator_generate(codep, retval,
+				       t->u.h.comptag,
+				       t->u.h.relation,
+				       t->u.h.comparator);
+	if (codep == -1) return -1;
+      
+	/* headers */
+	codep = bc_stringlist_generate(codep, retval, t->u.h.sl);
+	if (codep == -1) return -1;
+      
+	/* pattern */
+	codep = bc_stringlist_generate(codep, retval, t->u.h.pl);
+	if (codep == -1) return -1;
+	break;
+    case ADDRESS:
+    case ENVELOPE:
+	/* (BC_ADDRESS | BC_ENVELOPE) {c : comparator} 
+	   (B_ALL | B_LOCALPART | ...) { header : string list }
+	   { pattern : string list } */
+      
+	if(!atleast(retval,codep+1)) return -1;
+      
+	retval->data[codep++].op = (t->type == ADDRESS)
+	    ? BC_ADDRESS : BC_ENVELOPE;
+            
+	codep = bc_comparator_generate(codep, retval,t->u.ae.comptag,
+				       t->u.ae.relation, 
+				       t->u.ae.comparator);
+	if (codep == -1) return -1;
+
+	if(!atleast(retval,codep+1)) return -1;
+
+	/*address part*/
+	switch(t->u.ae.addrpart) {
+	case ALL:
+	    retval->data[codep++].value = B_ALL;
+	    break;
+	case LOCALPART:
+	    retval->data[codep++].value = B_LOCALPART;
+	    break;
+	case DOMAIN:
+	    retval->data[codep++].value = B_DOMAIN;
+	    break;
+	case USER:
+	    retval->data[codep++].value = B_USER;
+	    break;
+	case DETAIL:
+	    retval->data[codep++].value = B_DETAIL;
+	    break;
+	default:
+	    return -1;
+	}
+
+	/*headers*/
+	codep = bc_stringlist_generate(codep, retval, t->u.ae.sl);
+	if (codep == -1) return -1;
+
+	/*patterns*/
+	codep = bc_stringlist_generate(codep, retval, t->u.ae.pl);
+	if (codep == -1) return -1;
+     
+	break;
+    default:
+	return -1;
+      
+    }
+    return codep;
+}
+
+
+/* generate a not-quite-flattened bytecode */
+/* returns address of next instruction or -1 on error*/
+/* needs current instruction, buffer for the code, and a current parse tree */
+/* sieve is cool because everything is immediate! */
+static int bc_action_generate(int codep, bytecode_info_t *retval,
+			      commandlist_t *c) 
+{
+    int jumploc,baseloc;
+
+    if(!retval) return -1;
+    if (c==NULL)
+    {
+	if(!atleast(retval,codep+1)) return -1;
+	retval->data[codep++].op = B_NULL;
+    }
+    else
+    {
+	do {
+	    switch(c->type) {
+	    case STOP:
+		/* STOP (no arguments) */
+		if(!atleast(retval,codep+1)) return -1;
+		retval->data[codep++].op = B_STOP;
+		break;
+	    case DISCARD:
+		/* DISCARD (no arguments) */
+		if(!atleast(retval,codep+1)) return -1;
+		retval->data[codep++].op = B_DISCARD;
+		break;
+	    case KEEP:
+		/* KEEP (no arguments) */
+		if(!atleast(retval,codep+1)) return -1;
+		retval->data[codep++].op = B_KEEP;
+		break;
+	    case MARK:
+		/* MARK (no arguments) */
+		if(!atleast(retval,codep+1)) return -1;
+		retval->data[codep++].op = B_MARK;
+		break;
+	    case UNMARK:
+		/* UNMARK (no arguments) */
+		if(!atleast(retval,codep+1)) return -1;
+		retval->data[codep++].op = B_UNMARK;
+		break;
+	    case DENOTIFY:
+		/* DENOTIFY  */
+		if(!atleast(retval,codep+6)) return -1;
+		retval->data[codep++].op = B_DENOTIFY;
+		switch(c->u.d.priority) {
+		case LOW:
+		    retval->data[codep++].value = B_LOW;
+		    break;
+		case NORMAL:
+		    retval->data[codep++].value = B_NORMAL;
+		    break;
+		case HIGH:
+		    retval->data[codep++].value = B_HIGH;
+		    break;
+		case ANY:
+		    retval->data[codep++].value = B_ANY;
+		    break;
+		default:
+		    return -1;
+		}
+		switch(c->u.d.comptag) {
+		case IS:
+		    retval->data[codep++].value = B_IS;
+		    break;
+		case CONTAINS:
+		    retval->data[codep++].value = B_CONTAINS;
+		    break;
+		case MATCHES:
+		    retval->data[codep++].value = B_MATCHES;
+		    break;
+#ifdef ENABLE_REGEX
+		case REGEX:
+		    retval->data[codep++].value = B_REGEX;
+		    break;
+#endif
+		case ANY:
+		    retval->data[codep++].value = B_ANY;
+		    break; 
+		default:
+		    return -1;
+		}
+		codep = bc_relation_generate(codep, retval, c->u.d.relation);
+	
+		if(c->u.d.pattern)
+		{
+		    retval->data[codep++].len = strlen(c->u.d.pattern);
+		    retval->data[codep++].str = c->u.d.pattern;
+		} else {
+		    retval->data[codep++].len = -1;
+		    retval->data[codep++].str = NULL;
+		}
+
+		break;
+	    case REJCT:
+		/* REJECT (STRING: len + dataptr) */
+		if(!atleast(retval,codep+3)) return -1;
+		retval->data[codep++].op = B_REJECT;
+		retval->data[codep++].len = strlen(c->u.str);
+		retval->data[codep++].str = c->u.str;
+		break;
+	    case FILEINTO:
+		/* FILEINTO (STRING: len + dataptr) */
+		if(!atleast(retval,codep+3)) return -1;
+		retval->data[codep++].op = B_FILEINTO;
+		retval->data[codep++].len = strlen(c->u.str);
+		retval->data[codep++].str = c->u.str;
+		break;
+	    case REDIRECT:
+		/* REDIRECT (STRING: len + dataptr) */
+		if(!atleast(retval,codep+3)) return -1;
+		retval->data[codep++].op = B_REDIRECT;
+		retval->data[codep++].len = strlen(c->u.str);
+		retval->data[codep++].str = c->u.str;
+		break;
+	    case ADDFLAG:
+		/* ADDFLAG stringlist */
+		if(!atleast(retval,codep+1)) return -1;
+		retval->data[codep++].op = B_ADDFLAG;
+		codep = bc_stringlist_generate(codep,retval,c->u.sl);
+
+		if(codep == -1) return -1;
+		break;
+	    case SETFLAG:
+		/* SETFLAG stringlist */
+		if(!atleast(retval,codep+1)) return -1;
+		retval->data[codep++].op = B_SETFLAG;
+		codep = bc_stringlist_generate(codep,retval,c->u.sl);
+
+		if(codep == -1) return -1;
+		break;
+	    case REMOVEFLAG:
+		/* REMOVEFLAG stringlist */
+		if(!atleast(retval,codep+1)) return -1;
+		retval->data[codep++].op = B_REMOVEFLAG;
+		codep = bc_stringlist_generate(codep,retval,c->u.sl);
+
+		if(codep == -1) return -1;
+		break;
+	    case NOTIFY:
+		/* NOTIFY 
+		   (STRING: len + dataptr)
+		   (STRING: len + dataptr)
+		   stringlist
+		   (STRING: len + dataptr)
+		   (STRING: len + dataptr)
+		   method/id /options list/priority/message 
+		*/
+			
+		if(!atleast(retval,codep+5)) return -1;
+		retval->data[codep++].op = B_NOTIFY;
+		
+		retval->data[codep++].len = strlen(c->u.n.method);
+		retval->data[codep++].str = c->u.n.method;
+				
+		if (c->u.n.id)
+		{
+		    retval->data[codep++].len = strlen(c->u.n.id);
+		    retval->data[codep++].str = c->u.n.id;
+		}
+		else
+		{
+		    retval->data[codep++].len = -1;
+		    retval->data[codep++].str = NULL;
+		}
+		
+		codep = bc_stringlist_generate(codep,retval,c->u.n.options);
+		if(codep == -1) return -1;
+
+		if(!atleast(retval,codep+3)) return -1;
+
+		switch(c->u.n.priority) {
+		case LOW:
+		    retval->data[codep++].value = B_LOW;
+		    break;
+		case NORMAL:
+		    retval->data[codep++].value = B_NORMAL;
+		    break;
+		case HIGH:
+		    retval->data[codep++].value = B_HIGH;
+		    break;
+		case ANY:
+		    retval->data[codep++].value = B_ANY;
+		    break;
+		default:
+		    return -1;
+		}
+		
+		retval->data[codep++].len = strlen(c->u.n.message);
+		retval->data[codep++].str = c->u.n.message;
+		break;
+	    case VACATION:
+		/* VACATION
+		   STRINGLIST addresses
+		   STRING subject (if len is -1, then subject was NULL)
+		   STRING message (again, len == -1 means subject was NULL)
+		   VALUE days
+		   VALUE mime
+		*/
+
+		if(!atleast(retval,codep+1)) return -1;
+		retval->data[codep++].op = B_VACATION;
+	    
+		codep = bc_stringlist_generate(codep,retval,c->u.v.addresses);
+		if (codep == -1) return -1;
+
+		if (!atleast(retval,codep+2)) return -1;
+		if(c->u.v.subject) {
+		    retval->data[codep++].len = strlen(c->u.v.subject);
+		    retval->data[codep++].str = c->u.v.subject;
+		} else {
+		    retval->data[codep++].len = -1;
+		    retval->data[codep++].str = NULL;
+		}
+
+		if (!atleast(retval,codep+2)) return -1;
+		if(c->u.v.message) {
+		    retval->data[codep++].len = strlen(c->u.v.message);
+		    retval->data[codep++].str = c->u.v.message;
+		} else {
+		    retval->data[codep++].len = -1;
+		    retval->data[codep++].str = NULL;
+		}
+
+		if (!atleast(retval,codep+2)) return -1;
+		retval->data[codep++].value = c->u.v.days;
+		retval->data[codep++].value = c->u.v.mime;
+	    
+
+		if(codep == -1) return -1;
+		break;
+	    case IF:
+	    {
+		int jumpVal; 	    
+		/* IF
+		   (int: begin then block)
+		   (int: end then block/begin else block)
+		   (int:end else block) (-1 if no else block)
+		   (test)
+		   (then block)
+		   (else block)(optional)
+		*/
+		baseloc = codep;
+	    
+		/* Allocate operator + jump table offsets */
+		if(!atleast(retval,codep+4)) return -1;
+		
+		jumploc = codep+4;
+		retval->data[codep++].op = B_IF;
+		    
+		/* begining of then  code */
+		jumpVal= bc_test_generate(jumploc,retval,c->u.i.t);
+		if(jumpVal == -1) 
+		    return -1;
+		else {
+		    retval->data[codep].jump = jumpVal;
+		    codep++;
+		}
+	    
+		/* find then code and offset to else code,
+		 * we want to write this code starting at the offset we
+		 * just found */
+	
+		jumpVal= bc_action_generate(jumpVal,retval, c->u.i.do_then);
+		if(jumpVal == -1) 
+		    return -1;
+		else 
+		    retval->data[codep].jump = jumpVal;
+		
+		codep++;
+		/* write else code if its there*/
+		if(c->u.i.do_else) {
+	
+		    jumpVal= bc_action_generate(jumpVal,retval, c->u.i.do_else);
+		    if(jumpVal == -1) 
+		    {
+			return -1;
+		    } else 
+		    {
+			retval->data[codep].jump = jumpVal;
+		    }
+		    
+		    /* Update code pointer to end of else code */
+		    codep = retval->data[codep].jump;
+		} else {
+		    /*there is no else block, so its -1*/
+		    retval->data[codep].jump = -1;
+		    /* Update code pointer to end of then code */
+		    codep = retval->data[codep-1].jump;
+		}
+	    
+		break;
+	    }
+	    default:
+		/* no such action known */
+		return -1;
+	    }
+	  
+	    /* generate from next command */
+	    c = c->next;
+	} while(c);
+    }
+    /*scriptend may be updated before the end, but it will be updated at the end, which is what matters.*/
+    retval->scriptend=codep;
+    return codep;
+   
+}
+
+
+
+/* Entry point to the bytecode emitter module */	
+int sieve_generate_bytecode(bytecode_info_t **retval, sieve_script_t *s) 
+{
+    commandlist_t *c;
+
+    if(!retval) return -1;
+    if(!s) return -1;
+    c = s->cmds;
+    /* if c is NULL, it is handled in bc_action_generate and a script
+       with only BC_NULL is returned
+    */
+
+    
+    *retval = xmalloc(sizeof(bytecode_info_t));
+    if(!(*retval)) return -1;
+
+    memset(*retval, 0, sizeof(bytecode_info_t));
+
+    return bc_action_generate(0, *retval, c);
+}
+
+
+void sieve_free_bytecode(bytecode_info_t **p) 
+{
+    if(!p || !*p) return;
+    if((*p)->data) free((*p)->data);
+    free(*p);
+    *p = NULL;
+}
+ 
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/addr-lex.l
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/addr-lex.l
@@ -0,0 +1,91 @@
+%{
+/*
+ * addr-lex.l -- RFC 822 address lexer
+ * Ken Murchison
+ * $Id$
+ */
+/***********************************************************
+        Copyright 1999 by Carnegie Mellon University
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+******************************************************************/
+
+#include "addr.h"
+#include <string.h>
+
+#undef YY_INPUT
+#define YY_INPUT(b, r, ms) (r = addrinput(b, ms))
+
+int addrinput(char *buf, int max_size);
+void addrerror(const char *);
+
+static int ncom;	/* number of open comments */
+%}
+
+%option noyywrap
+%option nounput
+%option prefix="addr"
+
+%x QSTRING DOMAINLIT COMMENT
+
+%%
+
+\"				{ BEGIN QSTRING; return yytext[0]; }
+\[				{ BEGIN DOMAINLIT; return yytext[0]; }
+\(				{ ncom = 1; BEGIN COMMENT; }
+\)				{ addrerror("address parse error, "
+					  "unexpected `')'' "
+					  "(unbalanced comment)");
+				  yyterminate(); }
+
+[^\(\)<>@,;:\\".\[\] \n\r]+	return ATOM;
+
+[\t \n\r]+			/* ignore whitespace */
+.				return yytext[0];
+
+<QSTRING>([^\n\r"\\]|\\.)*	return QTEXT;
+<QSTRING>\"			{ BEGIN INITIAL; return yytext[0]; }
+
+<DOMAINLIT>([^\[\]\n\r\\]|\\.)*	return DTEXT;
+<DOMAINLIT>\]			{ BEGIN INITIAL; return yytext[0]; }
+
+<COMMENT>([^\(\)\n\0\\]|\\.)*	/* ignore comments */
+<COMMENT>\(			ncom++;
+<COMMENT>\)			{ ncom--; if (ncom == 0) BEGIN INITIAL; }
+<COMMENT><<EOF>>		{ addrerror("address parse error, "
+					  "expecting `')'' "
+					  "(unterminated comment)");
+				  yyterminate(); }
+
+%%
+
+/* take input from address string provided by sieve parser */
+int addrinput(char *buf, int max_size)
+{
+    extern char *addrptr;	/* current position in address string */
+    size_t n;			/* number of characters to read from string */
+
+    n = (int)strlen(addrptr) < max_size ? (int)strlen(addrptr) : max_size;
+    if (n > 0) {
+	memcpy(buf, addrptr, n);
+	addrptr += n;
+    }
+    return n;
+}
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/message.h
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/message.h
@@ -0,0 +1,140 @@
+/* message.h
+ * Larry Greenfield
+ * $Id$
+ */
+/***********************************************************
+        Copyright 1999 by Carnegie Mellon University
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+******************************************************************/
+
+#ifndef MESSAGE_H
+#define MESSAGE_H
+
+#include "sieve_interface.h"	/* for action contexts */
+#include "tree.h"		/* for stringlist_t */
+
+typedef struct Action action_list_t;
+
+typedef enum {
+    ACTION_NULL = -1,
+    ACTION_NONE = 0,
+    ACTION_REJECT,
+    ACTION_FILEINTO,
+    ACTION_KEEP,
+    ACTION_REDIRECT,
+    ACTION_DISCARD,
+    ACTION_VACATION,
+    ACTION_SETFLAG,
+    ACTION_ADDFLAG,
+    ACTION_REMOVEFLAG,
+    ACTION_MARK,
+    ACTION_UNMARK,
+    ACTION_NOTIFY,
+    ACTION_DENOTIFY
+} action_t;
+
+/* information */
+action_list_t *new_action_list(void);
+void free_action_list(action_list_t *actions);
+
+/* invariant: always have a dummy element when free_action_list, param
+   and vac_subj are freed.  none of the others are automatically freed.
+
+   the do_action() functions should copy param */
+struct Action {
+    action_t a;
+    union {
+	sieve_reject_context_t rej;
+	sieve_fileinto_context_t fil;
+	sieve_keep_context_t keep;
+	sieve_redirect_context_t red;
+	struct {
+	    /* addr, fromaddr, subj - freed! */
+	    sieve_send_response_context_t send;
+	    sieve_autorespond_context_t autoresp;
+	} vac;
+	struct {
+	    const char *flag;
+	} fla;
+    } u;
+    char *param;		/* freed! */
+    struct Action *next;
+    char *vac_subj;		/* freed! */
+    char *vac_msg;
+    int vac_days;
+};
+
+typedef struct notify_list_s {
+    int isactive;
+    const char *id;
+    const char *method;
+    const char **options;
+    const char *priority;
+    const char *message;
+    struct notify_list_s *next;
+} notify_list_t;
+
+/* header parsing */
+typedef enum {
+    ADDRESS_ALL,
+    ADDRESS_LOCALPART,
+    ADDRESS_DOMAIN,
+    ADDRESS_USER,
+    ADDRESS_DETAIL
+} address_part_t;
+
+int parse_address(const char *header, void **data, void **marker);
+char *get_address(address_part_t addrpart, void **data, void **marker,
+		  int canon_domain);
+int free_address(void **data, void **marker);
+notify_list_t *new_notify_list(void);
+void free_notify_list(notify_list_t *n);
+
+/* actions; return negative on failure.
+ * these don't actually perform the actions, they just add it to the
+ * action list */
+int do_reject(action_list_t *m, const char *msg);
+int do_fileinto(action_list_t *m, const char *mbox,
+		sieve_imapflags_t *imapflags);
+int do_redirect(action_list_t *m, const char *addr);
+int do_keep(action_list_t *m, sieve_imapflags_t *imapflags);
+int do_discard(action_list_t *m);
+int do_vacation(action_list_t *m, char *addr, char *fromaddr,
+		char *subj, const char *msg, int days, int mime);
+int do_setflag(action_list_t *m, const char *flag);
+int do_addflag(action_list_t *m, const char *flag);
+int do_removeflag(action_list_t *m, const char *flag);
+int do_mark(action_list_t *m);
+int do_unmark(action_list_t *m);
+int do_notify(notify_list_t *n, const char *id,
+	      const char *method, const char **options,
+	      const char *priority, const char *message);
+int do_denotify(notify_list_t *n, comparator_t *comp, const void *pat,
+		void *comprock, const char *priority);
+
+/* execute some bytecode */
+int sieve_eval_bc(sieve_interp_t *i, const void *bc_in, unsigned int bc_len,
+		  void *m, sieve_imapflags_t * imapflags,
+		  action_list_t *actions,
+		  notify_list_t *notify_list,
+		  const char **errmsg);
+
+#endif
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/sievec.c
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/sievec.c
@@ -0,0 +1,273 @@
+/* sievec.c -- compile a sieve script to bytecode manually
+ * Rob Siemborski
+ * $Id$
+ */
+/*
+ * Copyright (c) 1999-2000 Carnegie Mellon University.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any other legal
+ *    details, please contact  
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "sieve_interface.h"
+
+#include "libconfig.h"
+#include "xmalloc.h"
+
+#include "script.h"
+#include <string.h> 
+#include <stdlib.h>
+#include <sys/file.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+struct et_list *_et_list = NULL;
+
+int is_script_parsable(FILE *stream, char **errstr, sieve_script_t **ret);
+
+#define TIMSIEVE_FAIL -1
+#define TIMSIEVE_OK 0
+
+int main(int argc, char **argv) 
+{
+    FILE *instream;
+    char *err = NULL;
+    sieve_script_t *s;
+    bytecode_info_t *bc;
+    int c, fd, usage_error = 0;
+
+    while ((c = getopt(argc, argv, "C:")) != EOF)
+	switch (c) {
+	default:
+	    usage_error = 1;
+	    break;
+	}
+
+    if (usage_error || (argc - optind) < 2) {
+	printf("Syntax: %s <filename> <outputfile>\n",
+	       argv[0]);
+	exit(1);
+    }
+
+    instream = fopen(argv[optind++],"r");
+    if(instream == NULL) {
+	printf("Unable to open %s for reading\n", argv[1]);
+	exit(1);
+    }
+    
+    if(is_script_parsable(instream, &err, &s) == TIMSIEVE_FAIL) {
+	if(err) {
+	    printf("Unable to parse script: %s\n", err);
+	} else {
+	    printf("Unable to parse script.\n");
+	}
+	 
+	exit(1);
+    }
+    
+    /* Now, generate the bytecode */
+    if(sieve_generate_bytecode(&bc, s) == -1) {
+	printf("bytecode generate failed\n");
+	exit(1);
+    }
+
+    /* Now, open the new file */
+    fd = open(argv[optind], O_CREAT | O_TRUNC | O_WRONLY, 0644);
+    if(fd < 0) {
+	printf("couldn't open bytecode output file\n");
+	exit(1);
+    }  
+
+    /* Now, emit the bytecode */
+    if(sieve_emit_bytecode(fd, bc) == -1) {
+	printf("bytecode emit failed\n");
+	exit(1);
+    }
+
+    close(fd);
+    
+    return 0;
+}
+
+/* to make larry's stupid functions happy :) */ 
+static void foo(void)
+{
+    i_fatal("stub function called");
+}
+sieve_vacation_t vacation = {
+    0,				/* min response */
+    0,				/* max response */
+    (sieve_callback *) &foo,	/* autorespond() */
+    (sieve_callback *) &foo	/* send_response() */
+};
+
+static int sieve_notify(void *ac __attr_unused__, 
+			void *interp_context __attr_unused__, 
+			void *script_context __attr_unused__,
+			void *message_context __attr_unused__,
+			const char **errmsg __attr_unused__)
+{
+    i_fatal("stub function called");
+    return SIEVE_FAIL;
+}
+
+static int mysieve_error(int lineno, const char *msg,
+			 void *i __attr_unused__, void *s)
+{
+    char buf[1024];
+    char **errstr = (char **) s;
+
+    snprintf(buf, 80, "line %d: %s\r\n", lineno, msg);
+    *errstr = xrealloc(*errstr, strlen(*errstr) + strlen(buf) + 30);
+    i_info("%s", buf);
+    strcat(*errstr, buf);
+
+    return SIEVE_OK;
+}
+
+/* end the boilerplate */
+
+/* returns TRUE or FALSE */
+int is_script_parsable(FILE *stream, char **errstr, sieve_script_t **ret)
+{
+    sieve_interp_t *i;
+    sieve_script_t *s;
+    int res;
+  
+    res = sieve_interp_alloc(&i, NULL);
+    if (res != SIEVE_OK) {
+	i_error("sieve_interp_alloc() returns %d\n", res);
+	return TIMSIEVE_FAIL;
+    }
+
+    res = sieve_register_redirect(i, (sieve_callback *) &foo);
+    if (res != SIEVE_OK) {
+	i_error("sieve_register_redirect() returns %d\n", res);
+	return TIMSIEVE_FAIL;
+    }
+    res = sieve_register_discard(i, (sieve_callback *) &foo);
+    if (res != SIEVE_OK) {
+	i_error("sieve_register_discard() returns %d\n", res);
+	return TIMSIEVE_FAIL;
+    }
+    res = sieve_register_reject(i, (sieve_callback *) &foo);
+    if (res != SIEVE_OK) {
+	i_error("sieve_register_reject() returns %d\n", res);
+	return TIMSIEVE_FAIL;
+    }
+    res = sieve_register_fileinto(i, (sieve_callback *) &foo);
+    if (res != SIEVE_OK) {
+	i_error("sieve_register_fileinto() returns %d\n", res);
+	return TIMSIEVE_FAIL;
+    }
+    res = sieve_register_keep(i, (sieve_callback *) &foo);
+    if (res != SIEVE_OK) {
+	i_error("sieve_register_keep() returns %d\n", res);
+	return TIMSIEVE_FAIL;
+    }
+
+    res = sieve_register_imapflags(i, NULL);
+    if (res != SIEVE_OK) {
+	i_error("sieve_register_imapflags() returns %d\n", res);
+	return TIMSIEVE_FAIL;
+    }
+
+    res = sieve_register_size(i, (sieve_get_size *) &foo);
+    if (res != SIEVE_OK) {
+	i_error("sieve_register_size() returns %d\n", res);
+	return TIMSIEVE_FAIL;
+    }
+  
+    res = sieve_register_header(i, (sieve_get_header *) &foo);
+    if (res != SIEVE_OK) {
+	i_error("sieve_register_header() returns %d\n", res);
+	return TIMSIEVE_FAIL;
+    }
+  
+    res = sieve_register_envelope(i, (sieve_get_envelope *) &foo);
+    if (res != SIEVE_OK) {
+	i_error("sieve_register_envelope() returns %d\n", res);
+	return TIMSIEVE_FAIL;
+    }
+  
+    res = sieve_register_vacation(i, &vacation);
+    if (res != SIEVE_OK) {
+	i_error("sieve_register_vacation() returns %d\n", res);
+	return TIMSIEVE_FAIL;
+    }
+
+    res = sieve_register_notify(i, &sieve_notify);
+    if (res != SIEVE_OK) {
+	i_error("sieve_register_notify() returns %d\n", res);
+	return TIMSIEVE_FAIL;
+    }
+
+    res = sieve_register_parse_error(i, &mysieve_error);
+    if (res != SIEVE_OK) {
+	i_error("sieve_register_parse_error() returns %d\n", res);
+	return TIMSIEVE_FAIL;
+    }
+
+    rewind(stream);
+
+    *errstr = (char *) xmalloc(20 * sizeof(char));
+    strcpy(*errstr, "script errors:\r\n");
+
+    res = sieve_script_parse(i, stream, errstr, &s);
+
+    if (res == SIEVE_OK) {
+	if(ret) {
+	    *ret = s;
+	} else {
+	    sieve_script_free(&s);
+	}
+	free(*errstr);
+	*errstr = NULL;
+    }
+
+    /* free interpreter */
+    sieve_interp_free(&i);
+
+    return (res == SIEVE_OK) ? TIMSIEVE_OK : TIMSIEVE_FAIL;
+}
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/tree.c
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/tree.c
@@ -0,0 +1,224 @@
+/* tree.c -- abstract syntax tree handling
+ * Larry Greenfield
+ * $Id$
+ */
+/***********************************************************
+        Copyright 1999 by Carnegie Mellon University
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+******************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include "xmalloc.h"
+
+#include "tree.h"
+#include "sieve.h"
+
+stringlist_t *new_sl(char *s, stringlist_t *n)
+{
+    stringlist_t *p = (stringlist_t *) xmalloc(sizeof(stringlist_t));
+    p->s = s;
+    p->next = n;
+    return p;
+}
+
+
+tag_t *new_tag(int type, char *s)
+{
+    tag_t *p = (tag_t *) xmalloc(sizeof(tag_t));
+    p->type = type;
+    p->arg = s;
+    return p;
+}
+
+taglist_t *new_taglist(tag_t *t, taglist_t *n)
+{
+    taglist_t *p = (taglist_t *) xmalloc(sizeof(taglist_t));
+    p->t = t;
+    p->next = n;
+    return p;
+}
+
+test_t *new_test(int type) 
+{
+    test_t *p = (test_t *) xmalloc(sizeof(test_t));
+    p->type = type;
+    return p;
+}
+
+testlist_t *new_testlist(test_t *t, testlist_t *n)
+{
+    testlist_t *p = (testlist_t *) xmalloc(sizeof(testlist_t));
+    p->t = t;
+    p->next = n;
+    return p;
+}
+
+commandlist_t *new_command(int type)
+{
+    commandlist_t *p = (commandlist_t *) xmalloc(sizeof(commandlist_t));
+    p->type = type;
+    p->next = NULL;
+    return p;
+}
+
+commandlist_t *new_if(test_t *t, commandlist_t *y, commandlist_t *n)
+{
+    commandlist_t *p = (commandlist_t *) xmalloc(sizeof(commandlist_t));
+    p->type = IF;
+    p->u.i.t = t;
+    p->u.i.do_then = y;
+    p->u.i.do_else = n;
+    p->next = NULL;
+    return p;
+}
+
+void free_sl(stringlist_t *sl) 
+{
+    stringlist_t *sl2;
+    
+    while (sl != NULL) {
+	sl2 = sl->next;
+
+	if (sl->s) free(sl->s);
+
+	free(sl);
+	sl = sl2;
+    }
+}
+
+
+void free_test(test_t *t);
+
+static void free_tl(testlist_t *tl)
+{
+    testlist_t *tl2;
+
+    while (tl) {
+	tl2 = tl->next;
+
+	if (tl->t) free_test(tl->t);
+
+	free(tl);
+	tl = tl2;
+    }
+}
+
+void free_test(test_t *t)
+{
+    if (t == NULL) return;
+
+    switch (t->type) {
+    case ANYOF:
+    case ALLOF:
+	free_tl(t->u.tl);
+	break;
+
+    case EXISTS:
+	free_sl(t->u.sl);
+	break;
+
+    case SIZE:
+    case SFALSE:
+    case STRUE:
+	break;
+
+    case HEADER:
+	free_sl(t->u.h.sl);
+	free_sl(t->u.h.pl);
+	
+	break;
+
+    case ADDRESS:
+	free_sl(t->u.ae.sl);
+	free_sl(t->u.ae.pl);
+	break;
+
+    case NOT:
+	free_test(t->u.t);
+	break;
+    }
+
+    free(t);
+}
+
+void free_tree(commandlist_t *cl)
+{
+    commandlist_t *cl2;
+
+    while (cl != NULL) {
+	cl2 = cl->next;
+	switch (cl->type) {
+	case IF:
+	    free_test(cl->u.i.t);
+	    free_tree(cl->u.i.do_then);
+	    free_tree(cl->u.i.do_else);
+	    break;
+
+	case FILEINTO:
+	case REDIRECT:
+	case REJCT:
+	    if (cl->u.str) free(cl->u.str);
+	    break;
+
+	case VACATION:
+	    if (cl->u.v.subject) free(cl->u.v.subject);
+	    if (cl->u.v.addresses) free_sl(cl->u.v.addresses);
+	    if (cl->u.v.message) free(cl->u.v.message);
+	    break;
+	    
+	case SETFLAG:
+	case ADDFLAG:
+	case REMOVEFLAG:
+	    free_sl(cl->u.sl);
+	    break;
+
+	case KEEP:
+	case STOP:
+	case DISCARD:
+	    break;
+
+	case NOTIFY:
+	    if (cl->u.n.method) free(cl->u.n.method);
+	    if (cl->u.n.id) free(cl->u.n.id);
+	    if (cl->u.n.options) free_sl(cl->u.n.options);
+	    if (cl->u.n.message) free(cl->u.n.message);
+	    break;
+
+	case DENOTIFY:
+	    if (cl->u.d.pattern) {
+#ifdef ENABLE_REGEX
+		if (cl->u.d.comptag == REGEX) {
+		    regfree((regex_t *) cl->u.d.pattern);
+		}
+#endif
+		free(cl->u.d.pattern);
+	    }
+	    break;
+	}
+
+	free(cl);
+	cl = cl2;
+    }
+}
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/README
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/README
@@ -0,0 +1,59 @@
+$Id$
+
+CMU Sieve 2.1
+-------------
+
+This code is typically distributed as part of Cyrus imapd 1.6 and higher.
+This code will be configured and compiled from the cyrus-imapd directory.
+
+Notes on implementation
+-----------------------
+
+This is an implementation of a simple Sieve API.  This API is
+well-suited for incorporating in other programs, but is not
+extensible.  (If there is interest, we may implement an extensible API
+in the future.)
+
+If you wish to compile Sieve without compiling all of imapd, you'll
+have to create a Makefile for it.  I recommend you use Makefile.in as
+a guide.
+
+It should compile without libcyrus, but then it does not implement the
+"address" test.	 That's just too much work to do when I have a neato
+library to do it for me.
+
+There's a simple "test" application included, which is not built by
+default (type "make test" to build it).  It expects:
+
+test <message> <script>
+
+And prints out the actions taken or errors encountered.  (This
+implementation will attempt all the actions or no actions.)
+
+Questions and comments to:
+Derrick Brashear (shadow+sieve@andrew.cmu.edu)
+
+References:
+
+[SIEVE] Showalter, T., "Sieve: A Mail Filtering Language",
+RFC 3028, January, 2001.
+
+[VACATION] Showalter, T., "Sieve: Vacation Extension",
+draft-showalter-sieve-vacation-04.txt, August, 2000.
+
+[IMAPFLAGS] Melnikov, A., "Sieve -- IMAP flag extension",
+draft-melnikov-sieve-imapflags-03.txt, July, 2000.
+
+[NOTIFY] Martin, T., Segmuller, W.,
+"Sieve -- An extension for providing instant notifications",
+draft-martin-sieve-notify-01.txt, June, 2001.
+
+[REGEX] Murchison, K., "Sieve: Regular Expression Extension",
+draft-murchison-sieve-regex-04.txt, August, 2001.
+
+[RELATIONAL] Segmuller, W., "Sieve Extension: Relational Tests",
+RFC 3431, December 2002.
+
+[SUBADDR] Murchison, K., "Sieve Email Filtering -- Subaddress Extension",
+RFC 3598, September 2003.
+
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/script.h
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/script.h
@@ -0,0 +1,74 @@
+/* script.h -- script definition
+ * Larry Greenfield
+ * $Id$
+ */
+/***********************************************************
+        Copyright 1999 by Carnegie Mellon University
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+******************************************************************/
+
+#ifndef SIEVE_SCRIPT_H
+#define SIEVE_SCRIPT_H
+
+#include "sieve_interface.h"
+#include "interp.h"
+#include "tree.h"
+
+#define ADDRERR_SIZE 500
+
+struct sieve_script {
+    sieve_interp_t interp;
+
+    /* was a "require" done for these? */
+    struct sieve_support {
+	int fileinto       : 1;
+	int reject         : 1;
+	int envelope       : 1;
+	int vacation       : 1;
+	int imapflags      : 1;
+	int notify         : 1;
+	int regex          : 1;
+	int subaddress     : 1;
+	int relational     : 1;
+	int i_ascii_numeric: 1;
+    } support;
+
+    void *script_context;
+    commandlist_t *cmds;
+
+    int err;
+};
+
+struct sieve_bytecode
+{
+    sieve_interp_t *interp;
+    void *script_context;
+
+    const char *data;
+    unsigned long len;
+    int fd;
+};
+
+/* generated by the yacc script */
+commandlist_t *sieve_parse(sieve_script_t *script, FILE *f);
+int script_require(sieve_script_t *s, char *req);
+
+#endif
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/AUTHORS
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/AUTHORS
@@ -0,0 +1,9 @@
+$Id$
+
+Larry Greenfield <leg+sieve@andrew.cmu.edu> wrote the first pass.
+
+Alexy Melnikov <alexey.melnikov@isode.com> submitted some bug fixes and 
+improvements.
+
+Ken Murchison <ken@oceana.com> took the ball, added more extensions
+than existed in the known world, and overall improved the code mightily.
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/addr.y
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/addr.y
@@ -0,0 +1,91 @@
+%{
+/*
+ * addr.y -- RFC 822 address parser
+ * Ken Murchison
+ * $Id$
+ */
+/***********************************************************
+        Copyright 1999 by Carnegie Mellon University
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+******************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "addr.h"
+#include "script.h"
+#include "xmalloc.h"
+    
+int addrerror(char *msg);
+extern int yylex(void);
+
+#define YYERROR_VERBOSE /* i want better error messages! */
+%}
+
+%token ATOM QTEXT DTEXT
+
+%%
+sieve_address: addrspec			/* simple address */
+	| phrase '<' addrspec '>'	/* name & addr-spec */
+	;
+
+addrspec: localpart '@' domain		/* global-address */
+	;
+
+localpart: word				/* uninterpreted, case-preserved */
+	| word '.' localpart
+	;
+
+domain: subdomain
+	| subdomain '.' domain
+	;
+
+subdomain: domainref
+	| domainlit
+	;
+
+domainref: ATOM				/* symbolic reference */
+	;
+
+domainlit: '[' DTEXT ']'
+	;
+
+phrase: word
+	| word phrase
+	;
+
+word: ATOM
+	| qstring
+	;
+
+qstring: '"' QTEXT '"'
+	;
+
+%%
+
+/* copy address error message into buffer provided by sieve parser */
+int addrerror(char *s)
+{
+    extern char addrerr[ADDRERR_SIZE];
+    
+    strlcpy(addrerr, s, sizeof(addrerr));
+    return 0;
+}
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/sieve_err.et
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/sieve_err.et
@@ -0,0 +1,31 @@
+# sieve_err.et -- Error codes for the Sieve subsystem
+# $Id$
+#
+# Copyright 2000 Carnegie Mellon University
+# 
+error_table siev
+
+ec SIEVE_FAIL,
+   "Generic Sieve error"
+
+ec SIEVE_NOT_FINALIZED,
+   "Sieve interpretor not finalized"
+
+ec SIEVE_PARSE_ERROR,
+   "Parse error in Sieve script"
+
+ec SIEVE_RUN_ERROR,
+   "Run-time error during Sieve execution"
+
+ec SIEVE_INTERNAL_ERROR,
+   "Internal error in Sieve subsystem"
+
+ec SIEVE_NOMEM,
+   "Memory exhausted in Sieve subsystem"
+
+ec SIEVE_DONE,
+   "Sieve action already taken"
+
+end
+
+
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/script.c
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/script.c
@@ -0,0 +1,814 @@
+/* script.c -- sieve script functions
+ * Larry Greenfield
+ * $Id$
+ */
+/***********************************************************
+        Copyright 1999 by Carnegie Mellon University
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+******************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include "xmalloc.h"
+
+#include "md5.h"
+#include "sieve_interface.h"
+#include "interp.h"
+#include "script.h"
+#include "tree.h"
+#include "map.h"
+#include "sieve.h"
+#include "message.h"
+#include "bytecode.h"
+
+/* does this interpretor support this requirement? */
+int script_require(sieve_script_t *s, char *req)
+{
+    if (!strcmp("fileinto", req)) {
+	if (s->interp.fileinto) {
+	    s->support.fileinto = 1;
+	    return 1;
+	} else {
+	    return 0;
+	}
+    } else if (!strcmp("reject", req)) {
+	if (s->interp.reject) {
+	    s->support.reject = 1;
+	    return 1;
+	} else {
+	    return 0;
+	}
+    } else if (!strcmp("envelope", req)) {
+	if (s->interp.getenvelope) {
+	    s->support.envelope = 1;
+	    return 1;
+	} else {
+	    return 0;
+	}
+    } else if (!strcmp("vacation", req)) {
+	if (s->interp.vacation) {
+	    s->support.vacation = 1;
+	    return 1;
+	} else {
+	    return 0;
+	}
+    } else if (!strcmp("imapflags", req)) {
+	if (s->interp.markflags->flag) {
+	    s->support.imapflags = 1;
+	    return 1;
+	} else {
+	    return 0;
+	}
+    } else if (!strcmp("notify",req)) {
+	if (s->interp.notify) {
+	    s->support.notify = 1;
+	    return 1;
+	} else {
+	    return 0;
+	}
+#ifdef ENABLE_REGEX
+    } else if (!strcmp("regex", req)) {
+	s->support.regex = 1;
+	return 1;
+#endif
+    } else if (!strcmp("subaddress", req)) {
+	s->support.subaddress = 1;
+	return 1;
+    } else if (!strcmp("relational", req)) {
+	s->support.relational = 1;
+	return 1;
+    } else if (!strcmp("comparator-i;octet", req)) {
+	return 1;
+    } else if (!strcmp("comparator-i;ascii-casemap", req)) {
+	return 1;
+    } else if (!strcmp("comparator-i;ascii-numeric", req)) {
+	s->support.i_ascii_numeric = 1;
+	return 1;
+    }
+    return 0;
+}
+
+/* given an interpretor and a script, produce an executable script */
+int sieve_script_parse(sieve_interp_t *interp, FILE *script,
+		       void *script_context, sieve_script_t **ret)
+{
+    sieve_script_t *s;
+    int res = SIEVE_OK;
+    extern int yylineno;
+
+    res = interp_verify(interp);
+    if (res != SIEVE_OK) {
+	return res;
+    }
+
+    s = (sieve_script_t *) xmalloc(sizeof(sieve_script_t));
+    s->interp = *interp;
+    s->script_context = script_context;
+    /* clear all support bits */
+    memset(&s->support, 0, sizeof(struct sieve_support));
+
+    s->err = 0;
+
+    yylineno = 1;		/* reset line number */
+    s->cmds = sieve_parse(s, script);
+    if (s->err > 0) {
+	if (s->cmds) {
+	    free_tree(s->cmds);
+	}
+	s->cmds = NULL;
+	res = SIEVE_PARSE_ERROR;
+    }
+
+    *ret = s;
+    return res;
+}
+
+static void free_imapflags(sieve_imapflags_t *imapflags)
+{
+    while (imapflags->nflags)
+	free(imapflags->flag[--imapflags->nflags]);
+    free(imapflags->flag);
+    
+    imapflags->flag = NULL;
+}
+  
+int sieve_script_free(sieve_script_t **s)
+{
+    if (*s) {
+	if ((*s)->cmds) {
+	    free_tree((*s)->cmds);
+	}
+	free(*s);
+    }
+
+    return SIEVE_OK;
+}
+ 
+#define GROW_AMOUNT 100
+
+static void add_header(sieve_interp_t *i, int isenv, char *header, 
+		       void *message_context, char **out, 
+		       int *outlen, int *outalloc)
+{
+    const char **h;
+    int addlen;
+    /* get header value */
+    if (isenv)
+	i->getenvelope(message_context, header, &h);	
+    else
+	i->getheader(message_context, header, &h);	
+
+    if (!h || !h[0])
+	return;
+
+    addlen = strlen(h[0]) + 1;
+
+    /* realloc if necessary */
+    if ( (*outlen) + addlen >= *outalloc)
+    {
+	*outalloc = (*outlen) + addlen + GROW_AMOUNT;
+	*out = xrealloc(*out, *outalloc);
+    }
+
+    /* add header value */
+    strcat(*out,h[0]);
+
+    *outlen += addlen;
+}
+
+static int fillin_headers(sieve_interp_t *i, const char *msg, 
+			  void *message_context, char **out, int *outlen)
+{
+    int allocsize = GROW_AMOUNT;
+    const char *c;
+    int n;
+
+    *out = xmalloc(GROW_AMOUNT);
+    *outlen = 0;
+    (*out)[0]='\0';
+
+    if (msg == NULL) return SIEVE_OK;
+
+    /* construct the message */
+    c = msg;
+    while (*c) {
+	/* expand variables */
+	if (!strncasecmp(c, "$from$", 6)) {
+	    add_header(i, 0 ,"From", message_context, out, outlen, &allocsize);
+	    c += 6;
+	}
+	else if (!strncasecmp(c, "$env-from$", 10)) {
+	    add_header(i, 1, "From", message_context, out, outlen, &allocsize);
+	    c += 10;
+	}
+	else if (!strncasecmp(c, "$subject$", 9)) {
+	    add_header(i, 0, "Subject", message_context, out, outlen, &allocsize);
+	    c += 9;
+	}
+	/* XXX need to do $text$ variables */
+	else {
+	    /* find length of plaintext up to next potential variable */
+	    n = strcspn(c+1, "$") + 1; /* skip opening '$' */
+	    /* realloc if necessary */
+	    if ( (*outlen) + n+1 >= allocsize) {
+		allocsize = (*outlen) + n+1 + GROW_AMOUNT;
+		*out = xrealloc(*out, allocsize);
+	    }
+	    /* copy the plaintext */
+	    strncat(*out, c, n);
+	    (*out)[*outlen+n]='\0';
+	    (*outlen) += n;
+	    c += n;
+	}
+    }
+
+    return SIEVE_OK;
+}
+
+static int sieve_addflag(sieve_imapflags_t *imapflags, const char *flag)
+{
+    int n;
+    /* search for flag already in list */
+    for (n = 0; n < imapflags->nflags; n++) {
+	if (!strcmp(imapflags->flag[n], flag))
+	    break;
+    }
+ 
+    /* add flag to list, iff not in list */
+    if (n == imapflags->nflags) {
+	imapflags->nflags++;
+	imapflags->flag =
+	    (char **) xrealloc((char *)imapflags->flag,
+			       imapflags->nflags*sizeof(char *));
+	imapflags->flag[imapflags->nflags-1] = xstrdup(flag);
+    }
+ 
+    return SIEVE_OK;
+}
+
+static int sieve_removeflag(sieve_imapflags_t *imapflags, const char *flag)
+{
+    int n;
+    /* search for flag already in list */
+    for (n = 0; n < imapflags->nflags; n++) {
+      if (!strcmp(imapflags->flag[n], flag))
+	break;
+    }
+    
+     /* remove flag from list, iff in list */
+    if (n < imapflags->nflags) 
+      {
+	free(imapflags->flag[n]);
+	imapflags->nflags--;
+	
+	for (; n < imapflags->nflags; n++)
+	  imapflags->flag[n] = imapflags->flag[n+1];
+	
+	if (imapflags->nflags)
+	  {imapflags->flag =
+	     (char **) xrealloc((char *)imapflags->flag,
+				imapflags->nflags*sizeof(char *));}
+	else
+	  {free(imapflags->flag);
+	  imapflags->flag=NULL;}
+      }
+    
+    return SIEVE_OK;
+}
+
+static int send_notify_callback(sieve_interp_t *interp, void *message_context, 
+				void * script_context, notify_list_t *notify, 
+				char *actions_string, const char **errmsg)
+{
+    sieve_notify_context_t nc;
+    char *out_msg, *build_msg;
+    int out_msglen;    
+    int ret;
+
+    assert(notify->isactive);
+
+    if (!notify->method || !notify->options ||
+	!notify->priority || !notify->message) {
+	return SIEVE_RUN_ERROR;
+    }
+
+    nc.method = notify->method;
+    nc.options = notify->options ? notify->options : NULL;
+    nc.priority = notify->priority;
+
+    fillin_headers(interp, notify->message, message_context, 
+		   &out_msg, &out_msglen);
+
+    build_msg = xmalloc(out_msglen + strlen(actions_string) + 30);
+
+    strcpy(build_msg, out_msg);
+    strcat(build_msg, "\n\n");
+    strcat(build_msg, actions_string);
+
+    nc.message = build_msg;
+
+    free(out_msg);
+
+    ret = interp->notify(&nc,
+			 interp->interp_context,
+			 script_context,
+			 message_context,
+			 errmsg);    
+
+    free(build_msg);
+
+    return ret;
+}
+
+static char *action_to_string(action_t action)
+{
+    switch(action)
+	{
+	case ACTION_REJECT: return "Reject";
+	case ACTION_FILEINTO: return "Fileinto";
+	case ACTION_KEEP: return "Keep";
+	case ACTION_REDIRECT: return "Redirect";
+	case ACTION_DISCARD: return "Discard";
+	case ACTION_VACATION: return "Vacation";
+	case ACTION_SETFLAG: return "Setflag";
+	case ACTION_ADDFLAG: return "Addflag";
+	case ACTION_REMOVEFLAG: return "Removeflag";
+	case ACTION_MARK: return "Mark";
+	case ACTION_UNMARK: return "Unmark";
+	case ACTION_NOTIFY: return "Notify";
+	case ACTION_DENOTIFY: return "Denotify";
+	default: return "Unknown";
+	}
+
+    return "Error!";
+}
+
+static char *sieve_errstr(int code)
+{
+    switch (code)
+	{
+	case SIEVE_FAIL: return "Generic Error";
+	case SIEVE_NOT_FINALIZED: return "Sieve not finalized";
+	case SIEVE_PARSE_ERROR: return "Parse error";
+	case SIEVE_RUN_ERROR: return "Run error";
+	case SIEVE_INTERNAL_ERROR: return "Internal Error";
+	case SIEVE_NOMEM: return "No memory";
+	default: return "Unknown error";
+	}
+
+    return "Error!";
+}
+
+#define HASHSIZE 16
+
+static int makehash(unsigned char hash[HASHSIZE],
+		    const char *s1, const char *s2)
+{
+    struct md5_context ctx;
+
+    md5_init(&ctx);
+    md5_update(&ctx, s1, strlen(s1));
+    md5_update(&ctx, s2, strlen(s2));
+    md5_final(&ctx, hash);
+
+    return SIEVE_OK;
+}
+
+
+/******************************bytecode functions*****************************
+ *****************************************************************************/
+
+/* Load a compiled script */
+int sieve_script_load(const char *fname, sieve_bytecode_t **ret) 
+{
+    struct stat sbuf;
+    sieve_bytecode_t *r;
+    int fd;
+   
+    if (!fname || !ret) return SIEVE_FAIL;
+    
+    fd = open(fname, O_RDONLY);
+    if (fd == -1) {
+	if (errno != ENOENT)
+	    i_error("IOERROR: can not open sieve script %s: %m", fname);
+	return SIEVE_FAIL;
+    }
+
+    if (fstat(fd, &sbuf) == -1) {
+	i_error("IOERROR: fstating sieve script %s: %m", fname);
+	close(fd);
+	return SIEVE_FAIL;
+    }
+
+    r = (sieve_bytecode_t *) xzmalloc(sizeof(sieve_bytecode_t));
+
+    r->fd = fd;
+    
+    map_refresh(fd, 1, &r->data, &r->len, sbuf.st_size, fname, "sievescript");
+
+    if ((r->len < (BYTECODE_MAGIC_LEN + 2*sizeof(bytecode_input_t))) ||
+	memcmp(r->data, BYTECODE_MAGIC, BYTECODE_MAGIC_LEN)) {
+	i_error("IOERROR: not a sieve bytecode file %s", fname);
+	sieve_script_unload(&r);
+	return SIEVE_FAIL;
+    }
+
+    *ret = r;
+    return SIEVE_OK;
+}
+
+
+
+int sieve_script_unload(sieve_bytecode_t **s) 
+{
+    if(s && *s) {
+	map_free(&((*s)->data), &((*s)->len));
+	close((*s)->fd);
+	free(*s);
+	*s = NULL;
+    } 
+    /*i added this else, i'm not sure why, but this function always returned SIEVE_FAIL*/
+    else
+      return SIEVE_FAIL;
+    return SIEVE_OK;
+}
+
+
+#define ACTIONS_STRING_LEN 4096
+
+static int do_sieve_error(int ret,
+			  sieve_interp_t *interp,
+			  void *script_context,
+			  void *message_context,
+			  sieve_imapflags_t * imapflags,
+			  action_list_t *actions,
+			  notify_list_t *notify_list,
+			  /* notify_action_t *notify_action,*/
+			  int lastaction,
+			  int implicit_keep,
+			  char *actions_string,
+			  const char *errmsg
+			  ) 
+{
+   if (ret != SIEVE_OK) {
+	if (lastaction == -1) /* we never executed an action */
+	    snprintf(actions_string+strlen(actions_string),
+		     ACTIONS_STRING_LEN-strlen(actions_string),
+		     "script execution failed: %s\n",
+		     errmsg ? errmsg : sieve_errstr(ret));
+	else
+	    snprintf(actions_string+strlen(actions_string),
+		     ACTIONS_STRING_LEN-strlen(actions_string),
+		     "%s action failed: %s\n",
+		     action_to_string(lastaction),
+		     errmsg ? errmsg : sieve_errstr(ret));
+    }
+ 
+   
+    /* Process notify actions */
+    if (interp->notify && notify_list) 
+      {
+	notify_list_t *n = notify_list;
+	int notify_ret = SIEVE_OK;
+	
+	while (n != NULL) 
+	  {
+	    if (n->isactive) 
+	      {
+	      lastaction = ACTION_NOTIFY;
+	       notify_ret = send_notify_callback(interp, message_context, 
+						script_context,n,
+						actions_string, &errmsg);
+	      ret |= notify_ret;
+	      }
+	    n = n->next;
+	  }
+	
+	if (notify_list) free_notify_list(notify_list);
+	notify_list = NULL;	/* don't try any notifications again */
+	
+	
+	if (notify_ret != SIEVE_OK) 
+	  return do_sieve_error(ret, interp, script_context, message_context,
+				imapflags, actions, notify_list, lastaction,
+				implicit_keep, actions_string, errmsg);
+      
+      }
+    
+    if ((ret != SIEVE_OK) && interp->err) {
+	char buf[1024];
+	if (lastaction == -1) /* we never executed an action */
+	    sprintf(buf, "%s", errmsg ? errmsg : sieve_errstr(ret));
+	else
+	    sprintf(buf, "%s: %s", action_to_string(lastaction),
+		    errmsg ? errmsg : sieve_errstr(ret));
+ 
+	ret |= interp->execute_err(buf, interp->interp_context,
+				   script_context, message_context);
+    }
+
+    if (implicit_keep) {
+	sieve_keep_context_t keep_context;
+	int keep_ret;
+	keep_context.imapflags = imapflags;
+ 
+	lastaction = ACTION_KEEP;
+	keep_ret = interp->keep(&keep_context, interp->interp_context,
+				script_context, message_context, &errmsg);
+	ret |= keep_ret;
+        if (keep_ret == SIEVE_OK)
+            snprintf(actions_string+strlen(actions_string),
+		     sizeof(actions_string)-strlen(actions_string),
+		     "Kept\n");
+	else {
+	    implicit_keep = 0;	/* don't try an implicit keep again */
+	    return do_sieve_error(ret, interp, script_context, message_context,
+				  imapflags, actions, notify_list, lastaction,
+				  implicit_keep, actions_string, errmsg);
+	}
+    }
+
+    if (actions)
+	free_action_list(actions);
+
+    return ret;
+}
+
+
+static int do_action_list(sieve_interp_t *interp,
+			  void *script_context,
+			  void *message_context,
+			  sieve_imapflags_t *imapflags,
+			  action_list_t *actions,
+			  notify_list_t *notify_list,
+			  /* notify_action_t *notify_action,*/
+			  char *actions_string,
+			  const char *errmsg) 
+{
+    action_list_t *a;
+    action_t lastaction = -1;
+    int ret = 0;
+    int implicit_keep = 0;
+    
+    strcpy(actions_string,"Action(s) taken:\n");
+  
+    /* now perform actions attached to m */
+    a = actions;
+    implicit_keep = 1;
+    while (a != NULL) {
+	lastaction = a->a;
+	errmsg = NULL;
+	switch (a->a) {
+	case ACTION_REJECT:
+	    implicit_keep = 0;
+	    if (!interp->reject)
+		return SIEVE_INTERNAL_ERROR;
+	    ret = interp->reject(&a->u.rej,
+				 interp->interp_context,
+				 script_context,
+				 message_context,
+				 &errmsg);
+	    
+	    if (ret == SIEVE_OK)
+		snprintf(actions_string+strlen(actions_string),
+			 sizeof(actions_string)-strlen(actions_string), 
+			 "Rejected with: %s\n", a->u.rej.msg);
+
+	    break;
+	case ACTION_FILEINTO:
+	    implicit_keep = 0;
+	    if (!interp->fileinto)
+		return SIEVE_INTERNAL_ERROR;
+	    ret = interp->fileinto(&a->u.fil,
+				   interp->interp_context,
+				   script_context,
+				   message_context,
+				   &errmsg);
+
+	    if (ret == SIEVE_OK)
+		snprintf(actions_string+strlen(actions_string),
+			 sizeof(actions_string)-strlen(actions_string),
+			 "Filed into: %s\n",a->u.fil.mailbox);
+	    break;
+	case ACTION_KEEP:
+	    implicit_keep = 0;
+	    if (!interp->keep)
+		return SIEVE_INTERNAL_ERROR;
+	    ret = interp->keep(&a->u.keep,
+			       interp->interp_context,
+			       script_context,
+			       message_context,
+			       &errmsg);
+	    if (ret == SIEVE_OK)
+		snprintf(actions_string+strlen(actions_string),
+			 sizeof(actions_string)-strlen(actions_string),
+			 "Kept\n");
+	    break;
+	case ACTION_REDIRECT:
+	    implicit_keep = 0;
+	    if (!interp->redirect)
+		return SIEVE_INTERNAL_ERROR;
+	    ret = interp->redirect(&a->u.red,
+				   interp->interp_context,
+				   script_context,
+				   message_context,
+				   &errmsg);
+	    if (ret == SIEVE_OK)
+		snprintf(actions_string+strlen(actions_string),
+			 sizeof(actions_string)-strlen(actions_string),
+			 "Redirected to %s\n", a->u.red.addr);
+	    break;
+	case ACTION_DISCARD:
+	    implicit_keep = 0;
+	    if (interp->discard) /* discard is optional */
+		ret = interp->discard(NULL, interp->interp_context,
+				      script_context,
+				      message_context,
+				      &errmsg);
+	    if (ret == SIEVE_OK)
+		snprintf(actions_string+strlen(actions_string),
+			 sizeof(actions_string)-strlen(actions_string),
+			 "Discarded\n");
+	    break;
+
+	case ACTION_VACATION:
+	    {
+		unsigned char hash[HASHSIZE];
+
+		if (!interp->vacation)
+		    return SIEVE_INTERNAL_ERROR;
+
+		/* first, let's figure out if we should respond to this */
+		ret = makehash(hash, a->u.vac.send.addr,
+			       a->u.vac.send.msg);
+
+		if (ret == SIEVE_OK) {
+		    a->u.vac.autoresp.hash = hash;
+		    a->u.vac.autoresp.len = HASHSIZE;
+		    ret = interp->vacation->autorespond(&a->u.vac.autoresp,
+							interp->interp_context,
+							script_context,
+							message_context,
+							&errmsg);
+		}
+		if (ret == SIEVE_OK) {
+		    /* send the response */
+		    ret = interp->vacation->send_response(&a->u.vac.send,
+							  interp->interp_context,
+							  script_context, 
+							  message_context,
+							  &errmsg);
+
+		    if (ret == SIEVE_OK)
+			snprintf(actions_string+strlen(actions_string),
+				 sizeof(actions_string)-strlen(actions_string),
+				 "Sent vacation reply\n");
+
+		} else if (ret == SIEVE_DONE) {
+		    snprintf(actions_string+strlen(actions_string),
+			     sizeof(actions_string)-strlen(actions_string),
+			     "Vacation reply suppressed\n");
+
+		    ret = SIEVE_OK;
+		}
+	    
+		break;
+	    }
+
+ 
+	case ACTION_SETFLAG:
+	    free_imapflags(imapflags);
+	    ret = sieve_addflag(imapflags, a->u.fla.flag);
+	    break;
+	case ACTION_ADDFLAG:
+	    ret = sieve_addflag(imapflags, a->u.fla.flag);
+	    break;
+	case ACTION_REMOVEFLAG:
+	    ret = sieve_removeflag(imapflags, a->u.fla.flag);
+	    break;
+	case ACTION_MARK:
+	    {
+		int n = interp->markflags->nflags;
+
+		ret = SIEVE_OK;
+		while (n && ret == SIEVE_OK) {
+		    ret = sieve_addflag(imapflags,
+					interp->markflags->flag[--n]);
+		}
+		break;
+	    }
+	case ACTION_UNMARK:
+	  {
+	   
+		int n = interp->markflags->nflags;
+		ret = SIEVE_OK;
+		while (n && ret == SIEVE_OK) {
+		    ret = sieve_removeflag(imapflags,
+					   interp->markflags->flag[--n]);
+		}
+		break;
+	    }
+
+	case ACTION_NONE:
+	    break;
+
+	default:
+	    ret = SIEVE_INTERNAL_ERROR;
+	    break;
+	}
+	a = a->next;
+
+	if (ret != SIEVE_OK) {
+	    /* uh oh! better bail! */
+	    break;
+	}
+    }
+
+    return do_sieve_error(ret, interp, script_context, message_context, 
+			  imapflags, actions, notify_list, lastaction, 
+			  implicit_keep, actions_string, errmsg);
+}
+
+
+int sieve_execute_bytecode(sieve_bytecode_t *bc, sieve_interp_t *interp,
+			   void *script_context, void *message_context) 
+{
+    action_list_t *actions = NULL;
+    notify_list_t *notify_list = NULL;
+    /*   notify_action_t *notify_action;*/
+    action_t lastaction = -1;
+    int ret;
+    char actions_string[ACTIONS_STRING_LEN] = "";
+    const char *errmsg = NULL;
+    sieve_imapflags_t imapflags;
+    
+    if (!interp) return SIEVE_FAIL;
+
+    imapflags.flag = NULL; 
+    imapflags.nflags = 0;
+    
+    if (interp->notify)
+    {
+	notify_list = new_notify_list();
+	if (notify_list == NULL)
+	    {
+		ret = SIEVE_NOMEM;
+		return do_sieve_error(ret, interp, script_context,
+				      message_context, &imapflags,
+				      actions, notify_list, lastaction, 0,
+				      actions_string, errmsg);
+	    }
+    }
+
+    actions = new_action_list();
+    if (actions == NULL) 
+    {
+	ret = SIEVE_NOMEM;
+	return do_sieve_error(ret, interp, script_context,
+			      message_context, &imapflags,
+			      actions, notify_list, lastaction, 0,
+			      actions_string, errmsg);
+    }
+    
+    if (sieve_eval_bc(interp, bc->data, bc->len, message_context, 
+		      &imapflags, actions, notify_list, &errmsg) < 0)
+    {
+	ret = SIEVE_RUN_ERROR;
+	return do_sieve_error(ret, interp, script_context,
+			      message_context, &imapflags,
+			      actions, notify_list, lastaction, 0,
+			      actions_string, errmsg);
+    }
+    
+    return do_action_list(interp, script_context, message_context, 
+			  &imapflags, actions, notify_list, actions_string,
+			  errmsg);
+}
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/bc_emit.c
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/bc_emit.c
@@ -0,0 +1,672 @@
+/* bc_emit.c -- sieve bytecode - pass 2 of the compiler
+ * Rob Siemborski
+ * Jen Smith
+ * $Id$
+ */
+/***********************************************************
+        Copyright 2001 by Carnegie Mellon University
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+******************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "xmalloc.h"
+#include "sieve_interface.h"
+
+ 
+#include "bytecode.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+
+
+#if DUMPCODE
+void dump(bytecode_info_t *d);
+#endif
+
+static inline int write_int (int fd, int x)
+{
+    int y=htonl(x);
+    return (write(fd, &y, sizeof(int)));
+}
+ 
+    
+
+struct bytecode_info 
+{
+    bytecode_t *data;/* pointer to almost-flat bytecode */
+    size_t scriptend; /* used by emit code to know final length of bytecode  */
+    size_t reallen; /* allocated length of 'data' */
+};
+
+/* Pad null bytes onto the end of the string we just wrote */
+/* returns -1 on failure or number of bytes written on success */
+static int align_string(int fd, int string_len) 
+{
+    /* Keep in mind that we always want to pad a string with *at least*
+     * one zero, that's why sometimes we have to pad with 4 */
+    int needed = sizeof(int) - (string_len % sizeof(int));
+    if (needed>= 0 && needed <=4)
+    {
+    	if(write(fd, "\0\0\0\0", needed) == -1) return -1;
+    }
+    return needed;
+}
+
+/*all functions keep codep up to date as they use it.
+  the amount that has been written to the file is maintained by the
+  filelen variable in bc_action_emit
+  the other bc_xxx_emit funtions keep track of how much they (and any functions they call) have written and return this value
+*/
+
+
+/* Write out a stringlist to a given file descriptor.
+ * return # of bytes written on success and -1 on error */
+
+/* stringlist: <# listitems>
+               <pos of listend (bytes)>
+               <string:(size)(aligned string)>
+*/
+static int bc_stringlist_emit(int fd, int *codep, bytecode_info_t *bc) 
+{
+    int len = bc->data[(*codep)++].len;
+    int i;
+    int ret;
+    int wrote = 2*sizeof(int);
+    int begin,end;
+
+    /* Write out number of items in the list */
+    if (write_int(fd, len)== -1) return -1 ;
+    
+    /* skip one spot end of list position*/
+    begin=lseek(fd,0,SEEK_CUR);
+    lseek(fd,sizeof(int),SEEK_CUR);
+    
+    /* Loop through all the items of the list, writing out length and string
+     * in sequence */
+    for(i=0; i < len; i++)
+    {
+	int datalen = bc->data[(*codep)++].len;
+	
+	if(write_int(fd, datalen) == -1) return -1;
+	wrote += sizeof(int);
+	
+	if(write(fd, bc->data[(*codep)++].str, datalen) == -1) return -1;
+	wrote += datalen;
+	
+	ret = align_string(fd,datalen);
+	if(ret == -1) return -1;
+	
+	wrote+=ret;
+    }
+    end=lseek(fd,0,SEEK_CUR);
+ 
+    /* go back and write end of list position */
+    lseek(fd,begin,SEEK_SET);
+    if(write_int(fd, end) == -1) return -1;
+
+    /* return to the end */
+    lseek(fd,end,SEEK_SET);
+    return wrote;
+}
+
+static int bc_test_emit(int fd, int *codep, bytecode_info_t *bc);
+
+/* Write out a testlist to a given file descriptor.
+ * return # of bytes written on success and -1 on error */
+static int bc_testlist_emit(int fd, int *codep, bytecode_info_t *bc) 
+{
+    int len = bc->data[(*codep)++].len;
+    int i;
+    int ret;
+    int begin, end;
+    int wrote = 2*sizeof(int);
+        
+    /* Write out number of items in the list */
+    if(write_int(fd, len)== -1) return -1;
+
+    /* skip one spot for end of list position*/
+    begin = lseek(fd, 0, SEEK_CUR);
+    lseek(fd, sizeof(int), SEEK_CUR);
+      
+    /* Loop through all the items of the list, writing out each
+     * test as we reach it in sequence. */
+    for(i=0; i < len; i++) {
+	int nextcodep = bc->data[(*codep)++].jump;
+	
+	ret = bc_test_emit(fd, codep, bc);
+	if(ret < 0 ) return -1;
+	
+	wrote+=ret;
+	*codep = nextcodep;
+    }
+    end = lseek(fd, 0, SEEK_CUR);
+
+    /* go back and write the end of list position */
+    lseek(fd,begin,SEEK_SET);
+    if(write_int(fd, end) == -1) return -1;
+
+    /*return to the end */
+    lseek(fd,end,SEEK_SET);
+
+    return wrote;
+}
+
+/* emit the bytecode for a test.  returns -1 on failure or size of
+ * emitted bytecode on success */
+static int bc_test_emit(int fd, int *codep, bytecode_info_t *bc) 
+{
+    int wrote=0;/* Relative offset to account for interleaved strings */
+    
+    
+    int ret; /* Temporary Return Value Variable */
+    
+    /* Output this opcode */
+    if(write_int(fd, bc->data[(*codep)].op) == -1)
+	return -1;
+    wrote += sizeof(int);
+    
+    switch(bc->data[(*codep)++].op) {
+    case BC_TRUE:
+    case BC_FALSE:
+	/* No parameter opcodes */
+	break;
+	
+    case BC_NOT:
+    {
+	/* Single parameter: another test */
+	ret = bc_test_emit(fd, codep, bc);
+	if(ret < 0)
+	    return -1;
+	else
+	    wrote+=ret;
+	break;
+    }
+    
+    case BC_ALLOF:
+    case BC_ANYOF:
+	/*where we jump to?*/
+	/* Just drop a testlist */
+	ret = bc_testlist_emit(fd, codep, bc);
+	if(ret < 0)
+	    return -1;
+	else
+	    wrote+=ret;
+	break;
+	
+    case BC_SIZE:
+	/* Drop tag and number */
+	if(write_int(fd, bc->data[(*codep)].value) == -1)
+	    return -1;
+	if(write_int(fd, bc->data[(*codep)+1].value) == -1)
+	    return -1;
+	
+	wrote += 2 * sizeof(int);
+	(*codep) += 2;
+	break;
+	
+    case BC_EXISTS:
+    {
+	int ret;
+	ret = bc_stringlist_emit(fd, codep, bc);
+	if(ret < 0) return -1;
+	wrote += ret;
+	break;
+    }
+    
+    case BC_HEADER:
+    {
+	int ret;
+	/* Drop match type */
+	if(write_int(fd, bc->data[(*codep)].value) == -1)
+	    return -1;
+	wrote += sizeof(int);
+	(*codep)++;
+	/*drop comparator */
+	if(write_int(fd, bc->data[(*codep)].value) == -1)
+	    return -1;
+	wrote += sizeof(int);
+	(*codep)++;    
+	/*now drop relation*/
+	if(write_int(fd, bc->data[(*codep)].value) == -1)
+	    return -1;
+	wrote += sizeof(int);
+	(*codep)++;
+	/* Now drop headers */
+	ret = bc_stringlist_emit(fd, codep, bc);
+	if(ret < 0) return -1;
+	wrote+=ret;
+	/* Now drop data */
+	ret = bc_stringlist_emit(fd, codep, bc);
+	if(ret < 0) return -1;
+	wrote+=ret;
+	break;
+    }
+    
+    case BC_ADDRESS:
+    case BC_ENVELOPE:
+    {
+	int ret;
+	/* Drop match type */
+	if(write_int(fd, bc->data[(*codep)].value) == -1)
+	    return -1;
+	wrote += sizeof(int);
+	(*codep)++;
+	/*drop comparator */
+	if(write_int(fd, bc->data[(*codep)].value) == -1)
+	    return -1;
+	wrote += sizeof(int);
+	(*codep)++;
+	/*now drop relation*/
+	if(write_int(fd, bc->data[(*codep)].value) == -1)
+	    return -1;
+	wrote += sizeof(int);
+	(*codep)++;
+	/*now drop address part*/
+	if(write_int(fd, bc->data[(*codep)].value) == -1)
+	    return -1;
+	wrote += sizeof(int);
+	(*codep)++;
+	/* Now drop headers */
+	ret = bc_stringlist_emit(fd, codep, bc);
+	if(ret < 0) return -1;
+	wrote+=ret;
+	/* Now drop data */
+	ret = bc_stringlist_emit(fd, codep, bc);
+	if(ret < 0) return -1;
+	wrote+=ret;
+	break;
+    }
+    
+    default:
+	/* Unknown testcode? */
+	return -1;
+    }
+    return wrote;
+}
+
+/* emit the bytecode to a file descriptor given a flattened parse tree
+ * returns -1 on failure, size of emitted bytecode on success.
+ *
+ * this takes care of everything except the comparisons */
+static int bc_action_emit(int fd, int codep, int stopcodep,
+			  bytecode_info_t *bc, int filelen) 
+{
+    int len; /* Temporary Length Variable */
+    int ret; /* Temporary Return Value Variable */
+    int start_filelen = filelen;
+    int i;
+    
+    /*debugging variable to check filelen*/
+    /*int location;*/
+    
+    /*syslog(LOG_DEBUG, "entered bc_action_emit with filelen: %d", filelen);*/
+    
+    /* All non-string data MUST be sizeof(int)
+       byte alligned so the end of each string may require a pad */
+    /*
+     * Note that for purposes of jumps you must multiply codep by sizeof(int)
+     */
+    while(codep < stopcodep) {
+	/* Output this opcode */
+	if(write_int(fd, bc->data[codep].op) == -1)
+	    return -1; 
+	
+	filelen+=sizeof(int);
+	
+	switch(bc->data[codep++].op) {
+
+	case B_IF:
+	{
+	    /* IF
+	     *  test
+	     *  jump (false condition)
+	     *  then
+	     * (if there is an else) jump(finish) 
+	     * (if there is an else) else
+	     */
+
+	    int testEndLoc=-1;
+	    int testdist, thendist, elsedist;
+	    int c;
+	    
+	    int jumpFalseLoc=-1;/*this is the location that is being reserved
+				  for the first jump command
+				  we jump to the false condition of the test*/
+	    
+	    int jumpEndLoc=-1; /* this is the location that is being reserved
+				  for the optional jump command
+				  it jumps over the else statement to the end*/
+	    int jumpto=-1;
+	    int jumpop= B_JUMP;
+
+	    /*leave space to store the location of end of the test*/
+	    ret = lseek(fd, sizeof(int), SEEK_CUR);
+	    if(ret == -1) return ret;
+	    
+	    testEndLoc=filelen;
+	    filelen+=sizeof(int);
+	    
+	    /* spew the test */
+
+	    c=codep+3;
+	    testdist = bc_test_emit(fd, &c, bc);
+	    if(testdist == -1)return -1;
+	    filelen +=testdist;
+	    
+            /*store the location for hte end of the test
+	     *this is important for short circuiting of allof/anyof*/
+	    jumpto=filelen/4;
+	    if(lseek(fd, testEndLoc, SEEK_SET) == -1)
+		return -1;
+	    if(write_int(fd,jumpto) == -1)
+		return -1;
+
+	    if(lseek(fd,filelen,SEEK_SET) == -1)
+		return -1;
+
+	    /* leave space for jump */
+	    if(write_int(fd, jumpop) == -1)
+		return -1;
+	    ret = lseek(fd, sizeof(int), SEEK_CUR);
+	    if(ret == -1)
+		return ret;
+	    jumpFalseLoc=filelen+sizeof(int);
+	    
+	    filelen +=2*sizeof(int); /*jumpop + jump*/
+	    
+	    /* spew the then code */ 
+	    thendist = bc_action_emit(fd, bc->data[codep].value,
+				      bc->data[codep+1].value, bc,
+				      filelen);
+	 
+	    filelen+=thendist;
+	  	    
+	    /* there is an else case */
+	    if(bc->data[codep+2].value != -1)
+	    {
+		/* leave space for jump */
+		if(write_int(fd, jumpop) == -1)
+		    return -1;
+		ret = lseek(fd, sizeof(int), SEEK_CUR);
+		if(ret == -1)
+		    return ret;
+
+		jumpEndLoc=filelen+sizeof(int);
+		filelen+=2*sizeof(int);/*jumpop + jump*/
+	    }
+	  
+	    /*put previous jump to the end of the then code,
+	     *or the end of the jump if there is an else case */
+	    jumpto=filelen/4;
+	    if(lseek(fd, jumpFalseLoc, SEEK_SET) == -1)
+		return -1;
+	    if(write_int(fd,jumpto) == -1)
+		return -1;
+	    if(lseek(fd,filelen,SEEK_SET) == -1)
+		return -1;
+	    
+	    /* there is an else case */
+	    if(bc->data[codep+2].value != -1) {
+		/* spew the else code */
+		elsedist = bc_action_emit(fd, bc->data[codep+1].value,
+					 bc->data[codep+2].value, bc,
+					 filelen);
+	
+		filelen+=elsedist;
+		
+		/*put jump to the end of the else code*/
+	        jumpto=filelen/4;
+		if(lseek(fd, jumpEndLoc, SEEK_SET) == -1)
+		    return -1;
+		if(write_int(fd,jumpto) == -1)
+		    return -1;
+		if(lseek(fd,filelen,SEEK_SET) == -1)
+		    return -1;
+		
+		codep = bc->data[codep+2].value;
+	    } else {
+		codep = bc->data[codep+1].value;
+	    }
+	    
+	    break;
+	}
+	
+	case B_REJECT:
+	case B_FILEINTO:
+	case B_REDIRECT:
+	    /*just a string*/
+	    len = bc->data[codep++].len;
+	    if(write_int(fd,len) == -1)
+		return -1;
+
+	    filelen+=sizeof(int);
+	    
+	    if(write(fd,bc->data[codep++].str,len) == -1)
+		return -1;
+	    
+	    ret = align_string(fd, len);
+	    if(ret == -1)
+		return -1;
+
+	    filelen += len + ret;
+	    
+	    break; 
+
+	case B_SETFLAG:
+	case B_ADDFLAG:
+	case B_REMOVEFLAG:
+	    /* Dump just a stringlist */
+	    ret = bc_stringlist_emit(fd, &codep, bc);
+	    if(ret < 0)
+		return -1;
+	    filelen += ret;
+	    break;
+	    
+	case B_NOTIFY:
+	    /* method string, id string, options string list,
+	       priotity, Message String */
+	    /*method and id*/
+	    for(i=0; i<2; i++) {
+		len = bc->data[codep++].len;
+		if(write_int(fd,len) == -1)
+		    return -1;
+		filelen += sizeof(int);
+		if(len == -1)
+		{
+                    /* this will probably only happen for the id */
+		    /* this is a nil string */
+		    /* skip the null pointer and make up for it 
+		     * by adjusting the offset */
+		    codep++;
+		}
+		else
+		{	
+		    if(write(fd,bc->data[codep++].str,len) == -1)
+			return -1;
+		    
+		    ret = align_string(fd, len);
+		    if(ret == -1)
+			return -1;
+		    
+		    filelen += len + ret;
+		}
+		
+	    }
+	    /*options */
+	    ret = bc_stringlist_emit(fd, &codep, bc);
+	    if(ret < 0)
+		return -1;
+	    filelen+=ret;
+	    
+	    /*priority*/
+	    if(write_int(fd, bc->data[codep].value) == -1)
+		return -1;
+	    codep++;
+	    filelen += sizeof(int);
+	    
+	    len = bc->data[codep++].len;
+	    if(write_int(fd,len) == -1)
+		return -1;
+	    filelen += sizeof(int);
+	    
+	    if(write(fd,bc->data[codep++].str,len) == -1)
+		return -1;
+	    
+	    ret = align_string(fd, len);
+	    if(ret == -1) return -1;
+	    
+ 	    filelen += len + ret;
+	    break;
+
+		
+	case B_DENOTIFY:
+	    /* priority num,comptype  num,relat num, comp string*/ 
+
+	    /* priority*/
+	    if(write_int(fd, bc->data[codep].value) == -1)
+		return -1;
+	    filelen += sizeof(int);
+	    codep++;
+	    /* comptype */
+	    if(write_int(fd, bc->data[codep].value) == -1)
+		return -1;
+	    filelen += sizeof(int);
+	    codep++;
+	    /* relational*/
+	    if(write_int(fd, bc->data[codep].value) == -1)
+		return -1;
+	    filelen += sizeof(int);
+	    codep++;
+	    /* comp string*/
+	    
+	    len = bc->data[codep++].len;
+	    if(write_int(fd,len) == -1)
+		return -1;
+	    filelen += sizeof(int);
+	    
+	    if(len == -1)
+	    {
+		/* this is a nil string */
+		/* skip the null pointer and make up for it 
+		 * by adjusting the offset */
+		codep++;
+	    }
+	    else
+	    {
+		if(write(fd,bc->data[codep++].str,len) == -1)
+		    return -1;
+		
+		ret = align_string(fd, len);
+		if(ret == -1) return -1;
+		
+		filelen += len + ret;
+	    }
+	    	    break;
+	case B_VACATION:
+	    /* Address list, Subject String, Message String,
+	       Days (word), Mime (word) */
+	   
+	        /*new code-this might be broken*/
+	    ret = bc_stringlist_emit(fd, &codep, bc);
+	    if(ret < 0) return -1;
+	    filelen += ret;
+	    /*end of new code*/
+
+	    for(i=0; i<2; i++) {/*writing strings*/
+
+		/*write length of string*/
+		len = bc->data[codep++].len;
+		if(write_int(fd,len) == -1)
+		    return -1;
+		filelen += sizeof(int);
+		    
+		if(len == -1)
+		{
+		    /* this is a nil string */
+		    /* skip the null pointer and make up for it 
+		     * by adjusting the offset */
+		    codep++;
+		}
+		else
+		{
+		    /*write string*/
+		    if(write(fd,bc->data[codep++].str,len) == -1)
+			return -1;
+		    
+		    ret = align_string(fd, len);
+		    if(ret == -1) return -1;
+		    
+		    filelen += len + ret;
+		}
+		
+	    }
+	    /* Days*/
+	    if(write_int(fd,bc->data[codep].value) == -1)
+		return -1;
+	    codep++;
+	    filelen += sizeof(int);
+            /*Mime */
+	    if(write_int(fd,bc->data[codep].value) == -1)
+		return -1;
+	    codep++;
+	    filelen += sizeof(int);
+	    
+	    break;
+	case B_NULL:
+	case B_STOP:
+	case B_DISCARD:
+	case B_KEEP:
+	case B_MARK:
+	case B_UNMARK:
+	    /* No Parameters! */
+	    break;
+
+	default:
+	    /* Unknown opcode? */
+	    return -1;
+	}
+    }
+    return filelen - start_filelen;
+}
+
+/* spew the bytecode to disk */
+int sieve_emit_bytecode(int fd, bytecode_info_t *bc)  
+{
+    /* First output version number (4 bytes) */
+    int data = BYTECODE_VERSION;
+
+    /*this is a string, so it is happy*/
+    if(write(fd, BYTECODE_MAGIC, BYTECODE_MAGIC_LEN) == -1)
+	return -1;
+
+    if(write_int(fd, data) == -1) return -1;
+
+#if DUMPCODE
+    dump(bc);
+#endif
+
+    /*the sizeof(int) is to account for the version # at the begining*/
+    return bc_action_emit(fd, 0, bc->scriptend, bc, sizeof(int) + BYTECODE_MAGIC_LEN);
+}
+
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/message.c
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/message.c
@@ -0,0 +1,580 @@
+/* message.c -- message parsing functions
+ * Larry Greenfield
+ * $Id$
+ */
+/***********************************************************
+        Copyright 1999 by Carnegie Mellon University
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+******************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "sieve_interface.h"
+#include "interp.h"
+#include "message.h"
+#include "parseaddr.h"
+#include "xmalloc.h"
+
+/* reject message m with message msg
+ *
+ * incompatible with: fileinto, redirect
+ */
+int do_reject(action_list_t *a, const char *msg)
+{
+    action_list_t *b = NULL;
+
+    /* see if this conflicts with any previous actions taken on this message */
+    while (a != NULL) {
+	b = a;
+	if (a->a == ACTION_FILEINTO ||
+	    a->a == ACTION_KEEP ||
+	    a->a == ACTION_REDIRECT ||
+	    a->a == ACTION_REJECT ||
+	    a->a == ACTION_VACATION ||
+	    a->a == ACTION_SETFLAG ||
+	    a->a == ACTION_ADDFLAG ||
+	    a->a == ACTION_REMOVEFLAG ||
+	    a->a == ACTION_MARK ||
+	    a->a == ACTION_UNMARK
+	    )
+	    return SIEVE_RUN_ERROR;
+	a = a->next;
+    }
+
+    /* add to the action list */
+    a = (action_list_t *) xmalloc(sizeof(action_list_t));
+    if (a == NULL)
+	return SIEVE_NOMEM;
+    a->a = ACTION_REJECT;
+    a->u.rej.msg = msg;
+    b->next = a;
+    a->next =  NULL;
+    return 0;
+}
+
+/* fileinto message m into mailbox 
+ *
+ * incompatible with: reject
+ */
+int do_fileinto(action_list_t *a, const char *mbox,
+		sieve_imapflags_t *imapflags)
+{
+    action_list_t *b = NULL;
+
+    /* see if this conflicts with any previous actions taken on this message */
+    while (a != NULL) {
+	b = a;
+	if (a->a == ACTION_REJECT)
+	    return SIEVE_RUN_ERROR;
+	a = a->next;
+    }
+
+    /* add to the action list */
+    a = (action_list_t *) xmalloc(sizeof(action_list_t));
+    if (a == NULL)
+	return SIEVE_NOMEM;
+    a->a = ACTION_FILEINTO;
+    a->u.fil.mailbox = mbox;
+    a->u.fil.imapflags = imapflags;
+    b->next = a;
+    a->next = NULL;
+    return 0;
+}
+
+/* redirect message m to to addr
+ *
+ * incompatible with: reject
+ */
+int do_redirect(action_list_t *a, const char *addr)
+{
+    action_list_t *b = NULL;
+
+    /* xxx we should validate addr */
+
+    /* see if this conflicts with any previous actions taken on this message */
+    while (a != NULL) {
+	b = a;
+	if (a->a == ACTION_REJECT)
+	    return SIEVE_RUN_ERROR;
+	a = a->next;
+    }
+
+    /* add to the action list */
+    a = (action_list_t *) xmalloc(sizeof(action_list_t));
+    if (a == NULL)
+	return SIEVE_NOMEM;
+    a->a = ACTION_REDIRECT;
+    a->u.red.addr = addr;
+    a->next = NULL;
+    b->next = a;
+    return 0;
+}
+
+/* keep message
+ *
+ * incompatible with: reject
+ */
+int do_keep(action_list_t *a, sieve_imapflags_t *imapflags)
+{
+    action_list_t *b = NULL;
+
+    /* see if this conflicts with any previous actions taken on this message */
+    while (a != NULL) {
+	b = a;
+	if (a->a == ACTION_REJECT)
+	    return SIEVE_RUN_ERROR;
+	if (a->a == ACTION_KEEP) /* don't bother doing it twice */
+	    return 0;
+	a = a->next;
+    }
+
+    /* add to the action list */
+    a = (action_list_t *) xmalloc(sizeof(action_list_t));
+    if (a == NULL)
+	return SIEVE_NOMEM;
+    a->a = ACTION_KEEP;
+    a->u.keep.imapflags = imapflags;
+    a->next = NULL;
+    b->next = a;
+    return 0;
+}
+
+/* discard message m
+ *
+ * incompatible with: nothing---it doesn't cancel any actions
+ */
+int do_discard(action_list_t *a)
+{
+    action_list_t *b = NULL;
+
+    /* see if this conflicts with any previous actions taken on this message */
+    while (a != NULL) {
+	b = a;
+	if (a->a == ACTION_DISCARD) /* don't bother doing twice */
+	    return 0;
+	a = a->next;
+    }
+
+    /* add to the action list */
+    a = (action_list_t *) xmalloc(sizeof(action_list_t));
+    if (a == NULL)
+	return SIEVE_NOMEM;
+    a->a = ACTION_DISCARD;
+    a->next = NULL;
+    b->next = a;
+    return 0;
+}
+
+int do_vacation(action_list_t *a, char *addr, char *fromaddr,
+		char *subj, const char *msg, int days,
+		int mime)
+{
+    action_list_t *b = NULL;
+
+    /* see if this conflicts with any previous actions taken on this message */
+    while (a != NULL) {
+	b = a;
+	if (a->a == ACTION_REJECT ||
+	    a->a == ACTION_VACATION) /* vacation can't be used twice */
+	    return SIEVE_RUN_ERROR;
+	a = a->next;
+    }
+
+    /* add to the action list */
+    a = (action_list_t *) xmalloc(sizeof(action_list_t));
+    if (a == NULL)
+	return SIEVE_NOMEM;
+    a->a = ACTION_VACATION;
+    a->u.vac.send.addr = addr;
+    a->u.vac.send.fromaddr = fromaddr;
+    a->u.vac.send.subj = subj;	/* user specified subject */
+    a->u.vac.send.msg = msg;
+    a->u.vac.send.mime = mime;
+    a->u.vac.autoresp.days = days;
+    a->next = NULL;
+    b->next = a;
+    return 0;
+}
+
+/* setflag f on message m
+ *
+ * incompatible with: reject
+ */
+int do_setflag(action_list_t *a, const char *flag)
+{
+    action_list_t *b = NULL;
+ 
+    /* see if this conflicts with any previous actions taken on this message */
+    while (a != NULL) {
+	b = a;
+	if (a->a == ACTION_REJECT)
+	    return SIEVE_RUN_ERROR;
+	a = a->next;
+    }
+ 
+    /* add to the action list */
+    a = (action_list_t *) xmalloc(sizeof(action_list_t));
+    if (a == NULL)
+	return SIEVE_NOMEM;
+    a->a = ACTION_SETFLAG;
+    a->u.fla.flag = flag;
+    b->next = a;
+    a->next = NULL;
+    return 0;
+}
+
+/* addflag f on message m
+ *
+ * incompatible with: reject
+ */
+int do_addflag(action_list_t *a, const char *flag)
+{
+    action_list_t *b = NULL;
+ 
+    /* see if this conflicts with any previous actions taken on this message */
+    while (a != NULL) {
+	b = a;
+	if (a->a == ACTION_REJECT)
+	    return SIEVE_RUN_ERROR;
+	a = a->next;
+    }
+ 
+    /* add to the action list */
+    a = (action_list_t *) xmalloc(sizeof(action_list_t));
+    if (a == NULL)
+	return SIEVE_NOMEM;
+    a->a = ACTION_ADDFLAG;
+    a->u.fla.flag = flag;
+    b->next = a;
+    a->next = NULL;
+    return 0;
+}
+
+/* removeflag f on message m
+ *
+ * incompatible with: reject
+ */
+int do_removeflag(action_list_t *a, const char *flag)
+{
+    action_list_t *b = NULL;
+ 
+    /* see if this conflicts with any previous actions taken on this message */
+    while (a != NULL) {
+	b = a;
+	if (a->a == ACTION_REJECT)
+	    return SIEVE_RUN_ERROR;
+	a = a->next;
+    }
+ 
+    /* add to the action list */
+    a = (action_list_t *) xmalloc(sizeof(action_list_t));
+    if (a == NULL)
+	return SIEVE_NOMEM;
+    a->a = ACTION_REMOVEFLAG;
+    a->u.fla.flag = flag;
+    b->next = a;
+    a->next = NULL;
+    return 0;
+}
+
+
+/* mark message m
+ *
+ * incompatible with: reject
+ */
+int do_mark(action_list_t *a)
+{
+    action_list_t *b = NULL;
+ 
+    /* see if this conflicts with any previous actions taken on this message */
+    while (a != NULL) {
+	b = a;
+	if (a->a == ACTION_REJECT)
+	    return SIEVE_RUN_ERROR;
+	a = a->next;
+    }
+ 
+    /* add to the action list */
+    a = (action_list_t *) xmalloc(sizeof(action_list_t));
+    if (a == NULL)
+	return SIEVE_NOMEM;
+    a->a = ACTION_MARK;
+    b->next = a;
+    a->next = NULL;
+    return 0;
+}
+
+
+/* unmark message m
+ *
+ * incompatible with: reject
+ */
+int do_unmark(action_list_t *a)
+{
+
+    action_list_t *b = NULL;
+    /* see if this conflicts with any previous actions taken on this message */
+    while (a != NULL) {
+	b = a;
+	if (a->a == ACTION_REJECT)
+	    return SIEVE_RUN_ERROR;
+	a = a->next;
+    }
+ 
+    /* add to the action list */
+    a = (action_list_t *) xmalloc(sizeof(action_list_t));
+    if (a == NULL)
+	return SIEVE_NOMEM;
+    a->a = ACTION_UNMARK;
+    b->next = a;
+    a->next = NULL;
+    return 0;
+}
+
+/* notify
+ *
+ * incompatible with: none
+ */
+int do_notify(notify_list_t *a, const char *id,
+	      const char *method, const char **options,
+	      const char *priority, const char *message)
+{
+    notify_list_t *b = NULL;
+
+    /* find the end of the notify list */
+    while (a != NULL) {
+	b = a;
+	a = a->next;
+    }
+
+    /* add to the notify list */
+    a = (notify_list_t *) xmalloc(sizeof(notify_list_t));
+    if (a == NULL)
+	return SIEVE_NOMEM;
+
+    b->next = a;
+    a->isactive = 1;
+    a->id = id;
+    a->method = method;
+    a->options = options;
+    a->priority = priority;
+    a->message = message;
+    a->next = NULL;
+    return 0;
+}
+
+/* denotify
+ *
+ * incomaptible with: none
+ */
+int do_denotify(notify_list_t *n, comparator_t *comp, const void *pat,
+		void *comprock, const char *priority)
+{
+    while (n != NULL) {
+	if (n->isactive && 
+	    (!priority || !strcasecmp(n->priority, priority)) &&
+	    (!comp || (n->id && comp(n->id, pat, comprock)))) {
+	    n->isactive = 0;
+	}
+	n = n->next;
+    }
+
+    return 0;
+}
+
+
+
+/* given a header, extract an address out of it.  if marker points to NULL,
+   extract the first address.  otherwise, it's an index into the header to
+   say where to start extracting */
+struct addr_marker {
+    struct address *where;
+    char *freeme;
+};
+
+int parse_address(const char *header, void **data, void **marker)
+{
+    struct addr_marker *am = (struct addr_marker *) *marker;
+
+    parseaddr_list(header, (struct address **) data);
+    am = (void *) xmalloc(sizeof(struct addr_marker));
+    am->where = *data;
+    am->freeme = NULL;
+    *marker = am;
+    return SIEVE_OK;
+}
+
+char *get_address(address_part_t addrpart,
+		  void **data __attr_unused__,
+		  void **marker,
+		  int canon_domain)
+{
+    char *ret = NULL;
+    struct address *a;
+    struct addr_marker *am = *marker;
+
+    a = am->where;
+    if (am->freeme) {
+	free(am->freeme);
+	am->freeme = NULL;
+    }
+
+    if (a == NULL) {
+	ret = NULL;
+    } else {
+	if (canon_domain && a->domain)
+	    lcase(a->domain);
+
+	switch (addrpart) { 
+	case ADDRESS_ALL:
+#define U_DOMAIN "unspecified-domain"
+#define U_USER "unknown-user"
+	    if (a->mailbox || a->domain) {
+		char *m = a->mailbox ? a->mailbox : U_USER;
+		char *d = a->domain ? a->domain : U_DOMAIN;
+		am->freeme = (char *) xmalloc(strlen(m) + strlen(d) + 2);
+
+		sprintf(am->freeme, "%s@%s", m, d);
+		ret = am->freeme;
+	    } else {
+		ret = NULL;
+	    }
+	    break;
+
+	case ADDRESS_LOCALPART:
+	    ret = a->mailbox;
+	    break;
+	    
+	case ADDRESS_DOMAIN:
+	    ret = a->domain;
+	    break;
+
+	case ADDRESS_USER:
+	    if (a->mailbox) {
+		char *p = strchr(a->mailbox, '+');
+		int len = p ? p - a->mailbox : (int)strlen(a->mailbox);
+
+		am->freeme = (char *) xmalloc(len + 1);
+		strncpy(am->freeme, a->mailbox, len);
+		am->freeme[len] = '\0';
+		ret = am->freeme;
+	    } else {
+		ret = NULL;
+	    }
+	    break;
+
+	case ADDRESS_DETAIL:
+	    if (a->mailbox)
+	    {	    
+		char *p = strchr(a->mailbox, '+');
+		ret = (p ? p + 1 : NULL);
+	    }
+	    else
+	    {
+		ret = NULL;
+	    }
+	    break;
+	}
+	a = a->next;
+	am->where = a;
+    }
+    *marker = am;
+    return ret;
+}
+
+int free_address(void **data, void **marker)
+{
+    struct addr_marker *am = (struct addr_marker *) *marker;
+
+    if (*data)
+	parseaddr_free((struct address *) *data);
+    *data = NULL;
+    if (am->freeme) free(am->freeme);
+    free(am);
+    *marker = NULL;
+    return SIEVE_OK;
+}
+
+notify_list_t *new_notify_list(void)    
+{
+    notify_list_t *ret = xmalloc(sizeof(notify_list_t));
+
+    if (ret != NULL) {
+	ret->isactive = 0;
+	ret->id       = NULL;
+	ret->method   = NULL;
+	ret->options  = NULL;
+	ret->priority = NULL;
+	ret->message  = NULL;
+	ret->next     = NULL;
+    }
+    return ret;
+}
+
+void free_notify_list(notify_list_t *n)
+{
+    while (n) {
+	notify_list_t *b = n->next;
+	free(n->options); /* strings live in bytecode, only free the array */
+	free(n);
+	n = b;
+    }
+}
+
+action_list_t *new_action_list(void)
+{
+    action_list_t *ret = xmalloc(sizeof(action_list_t));
+
+    if (ret != NULL) {
+	ret->a = ACTION_NONE;
+	ret->param = NULL;
+	ret->next = NULL;
+    }
+    return ret;
+}
+
+void free_action_list(action_list_t *a)
+{
+    while (a) {
+	action_list_t *b = a->next;
+
+	if(a->a == ACTION_VACATION) {
+	    if(a->u.vac.send.subj) free(a->u.vac.send.subj);
+	    if(a->u.vac.send.addr) free(a->u.vac.send.addr);
+	    if(a->u.vac.send.fromaddr) free(a->u.vac.send.fromaddr);
+	}
+
+	free(a);
+	a = b;
+    }
+}
+
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/sieved.c
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/sieved.c
@@ -0,0 +1,437 @@
+/* dump.c -- bytecode decompiler
+ * Jen Smith
+ */
+/***********************************************************
+        Copyright 1999 by Carnegie Mellon University
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*****************************************************************/
+
+
+
+#include "sieve_interface.h"
+
+#include "bytecode.h"
+#include "script.h"
+
+#include "xmalloc.h"
+#include <sys/types.h> 
+#include <sys/stat.h>
+#include <fcntl.h> 
+#include <unistd.h> 
+#include <netinet/in.h>
+
+#include <string.h>
+
+#include "map.h"
+
+static void dump2(bytecode_input_t *d, int len);
+static int dump2_test(bytecode_input_t * d, int i);
+
+static int load(int fd, bytecode_input_t ** d)
+{  
+    const char * data=NULL;
+    struct stat sbuf;
+    unsigned long len=0;
+    
+    if (fstat(fd, &sbuf) == -1) {
+	printf("IOERROR: fstating sieve script: %m");
+	return SIEVE_FAIL;
+    }
+    
+    /*this reads in data and length from file*/
+    map_refresh(fd, 1, &(data), &len, sbuf.st_size,
+		"sievescript", "");
+    *d=(bytecode_input_t *)data;
+    
+    printf("\n");
+    
+    return (len/sizeof(int));
+}
+
+
+int main(int argc, char * argv[])
+{
+    bytecode_input_t * bc;
+    int script_fd;
+    
+    unsigned long len;
+    
+    if (argc!=2) {
+	 fprintf(stderr, "usage:\n %s script\n", argv[0]);
+	 exit(1);
+    }
+
+    /*get script*/
+    script_fd = open(argv[1], O_RDONLY);
+    if (script_fd == -1) 
+    {
+	printf("can not open script '%s'\n", argv[1]);
+	exit(1);
+    }
+    
+    lib_init();
+    len=load(script_fd,&bc);
+    close(script_fd);
+    
+    if (bc) {
+	dump2(bc, len );
+	exit(0);
+    } else {
+	exit(1);
+    }
+}
+
+static int write_list(int list_len, int i, bytecode_input_t * d)
+{
+    int x;
+    i++;
+    for (x=0; x<list_len; x++)
+    {
+	const char *data;
+	int len;
+	
+	i = unwrap_string(d, i, &data, &len);
+	
+	printf("{%d}%s\n", len, data);
+    }
+    return i;
+}
+
+static int printComparison(bytecode_input_t *d ,int i)
+{
+    printf("Comparison: ");
+    switch(ntohl(d[i].value))
+    {
+    case B_IS: printf("Is"); break;
+    case B_CONTAINS:printf("Contains"); break;
+    case B_MATCHES: printf("Matches"); break;
+    case B_REGEX: printf("Regex"); break;
+    case B_COUNT:
+	printf("Count");
+	
+	switch(ntohl(d[i+1].value))
+	{
+	case B_GT: printf(" greater than "); break;   
+	case B_GE: printf(" greater than or equal "); break;
+	case B_LT: printf(" less than "); break;
+	case B_LE: printf(" less than or equal "); break;
+	case B_NE: printf(" not equal "); break;
+	case B_EQ: printf(" equal "); break;
+	}
+
+	break;
+    case B_VALUE:
+	printf("Value");
+	
+	switch(ntohl(d[i+1].value))
+	{
+	case B_GT: printf(" greater than "); break;   
+	case B_GE: printf(" greater than or equal ");break;
+	case B_LT: printf(" less than ");    break;
+	case B_LE: printf(" less than or equal ");break;
+	case B_NE: printf(" not equal ");    break;
+	case B_EQ: printf(" equal ");break;
+	}
+	
+	break;
+    default:
+	exit(1);
+    }
+
+    switch (ntohl(d[i+2].value))
+    {
+    case B_ASCIICASEMAP: printf("   (ascii-casemap) "); break;
+    case B_OCTET: printf("    (octet) "); break;
+    case B_ASCIINUMERIC:  printf("   (ascii-numeric) "); break;
+    default: exit(1);
+    }
+    
+    printf("\n");
+    return i+3;
+}
+
+
+static int dump2_test(bytecode_input_t * d, int i)
+{
+    int l,x;
+    switch(ntohl(d[i].value)) {
+    case BC_FALSE:
+	printf("false");
+	i++;
+	break;
+    case BC_TRUE:
+	printf("true");
+	i++;
+	break;
+    case BC_NOT:/*2*/
+	/* XXX 
+	   there is a value being skipped in the second pass...
+	   no idea what it does, but it isn't carried to here...
+	   see bytecodee.c */
+	printf(" not(");
+	i=dump2_test(d, i+1);
+	printf(")\n");
+	break;
+    case BC_EXISTS:
+	printf("exists");
+	i=write_list(ntohl(d[i+1].len), i+2, d);
+	break;
+    case BC_SIZE:
+	printf("size");
+	if (ntohl(d[i+1].value)==B_OVER) {
+	    /* over */
+	    printf("over %d", ntohl(d[i+2].value));
+	} else {
+	    /* under */
+	    printf("under %d", ntohl(d[i+2].value));
+	}
+	i+=3;
+	break;
+    case BC_ANYOF:/*5*/
+	printf("any of \n(");
+	l=ntohl(d[i+1].len);
+	i+=3;
+	
+	for (x=0; x<l; x++)
+	{
+	    i=dump2_test(d,i);
+	    if((x+1)<l)
+		printf(" OR ");
+	}
+	
+	printf(")\n");	 
+	break;
+    case BC_ALLOF:/*6*/
+	printf("all of \n(");
+	l=ntohl(d[i+1].len);
+	i+=3;
+	
+	for (x=0; x<l; x++)
+	{
+	    i=dump2_test(d,i);
+	    if((x+1)<l)
+		printf(" AND ");
+	}
+	
+	printf(")\n");
+	break;
+    case BC_ADDRESS:/*7*/
+	printf("Address (");
+	i=printComparison(d, i+1);
+	printf("               type: ");
+	switch(ntohl(d[i++].value))
+	{
+	case B_ALL: printf("all"); break;
+	case B_LOCALPART:printf("localpart"); break;
+	case B_DOMAIN:printf("domain"); break;
+	case B_USER:printf("user"); break;
+	case B_DETAIL:printf("detail"); break;
+	}
+	printf("              Headers:");
+	i=write_list(ntohl(d[i].len), i+1, d);
+	printf("              Data:");
+	i=write_list(ntohl(d[i].len), i+1, d);
+	printf("             ]\n");
+	break;
+    case BC_ENVELOPE:/*8*/
+	printf("Envelope (");
+	i=printComparison(d, i+1);
+	printf("                type: ");
+	switch(ntohl(d[i++].value))
+	{
+	case B_ALL: printf("all"); break;
+	case B_LOCALPART:printf("localpart"); break;
+	case B_DOMAIN:printf("domain"); break;
+	case B_USER:printf("user"); break;
+	case B_DETAIL:printf("detail"); break;
+	}
+	printf("              Headers:");
+	i=write_list(ntohl(d[i].len), i+1, d);
+	printf("              Data:");
+	i=write_list(ntohl(d[i].len), i+1, d);
+	printf("             ]\n");
+	break;
+    case BC_HEADER:/*9*/
+	printf("Header [");
+	i= printComparison(d, i+1);
+	printf("              Headers: ");
+	i=write_list(ntohl(d[i].len), i+1, d);
+	printf("              Data: ");
+	i=write_list(ntohl(d[i].len), i+1, d);
+	printf("             ]\n");
+	break;
+    default:
+	printf("WERT %d ", ntohl(d[i].value));
+    }   
+    return i;
+}
+
+static void dump2(bytecode_input_t *d, int bc_len)
+{
+    int i;
+    const char *data;
+    int len;
+    
+    if(memcmp(d, BYTECODE_MAGIC, BYTECODE_MAGIC_LEN)) {
+	printf("not a bytecode file [magic number test failed]\n");
+	return;
+    }
+
+    i = BYTECODE_MAGIC_LEN / sizeof(bytecode_input_t);
+
+    printf("Sievecode version %d\n", ntohl(d[i].op));
+    if(!d) return;
+    
+    for(i++; i<bc_len;) 
+    {
+	switch(ntohl(d[i].op)) {
+	    
+	case B_STOP:/*0*/
+	    printf("%d: STOP\n",i);
+	    i++;
+	    break;
+	    
+	case B_KEEP:/*1*/
+	    printf("%d: KEEP\n",i);
+	    i++;
+	    break;
+	    
+	case B_DISCARD:/*2*/
+	    printf("%d: DISCARD\n",i);
+	    i++;
+	    break;
+	    
+	case B_REJECT:/*3*/
+	    i = unwrap_string(d, i+1, &data, &len);
+	    printf("%d: REJECT {%d}%s\n", i, len, data);
+	    break;
+
+	case B_FILEINTO: /*4*/
+	    i = unwrap_string(d, i+1, &data, &len);
+	    printf("%d: FILEINTO {%d}%s\n",i, len, data);
+	    break;
+
+	case B_REDIRECT: /*5*/
+	    i = unwrap_string(d, i+1, &data, &len);
+	    printf("%d: REDIRECT {%d}%s\n",i,len,data);
+	    break;
+	     
+	case B_IF:/*6*/
+	    printf("%d: IF (ends at %d)",i, ntohl(d[i+1].value));
+
+            /* there is no short circuiting involved here*/
+	    i = dump2_test(d,i+2);
+	    printf("\n");
+
+	    break;
+
+	case B_MARK:/*7*/
+	    printf("%d: MARK\n",i);
+	    i++;
+	    break;
+
+	case B_UNMARK:/*8*/
+	    printf("%d: UNMARK\n",i);
+	    i++;
+	    break;
+
+	case B_ADDFLAG: /*9*/
+	    printf("%d: ADDFLAG  {%d}\n",i,ntohl(d[i+1].len));
+	    i=write_list(ntohl(d[i+1].len),i+2,d);
+	    break;
+
+	case B_SETFLAG: /*10*/
+	    printf("%d: SETFLAG  {%d}\n",i,ntohl(d[i+1].len));
+	    i=write_list(ntohl(d[i+1].len),i+2,d);
+	    break;
+	    
+	case B_REMOVEFLAG: /*11*/
+	    printf("%d: REMOVEFLAG  {%d}\n",i,ntohl(d[i+1].len));
+	    i=write_list(ntohl(d[i+1].len),i+2,d);
+	    break;
+	    
+	case B_DENOTIFY:/*12*/
+	    printf("%d: DENOTIFY\n",i);
+	    i++; 
+	    printf("            PRIORITY(%d) Comparison type %d (relat %d)\n",
+		   ntohl(d[i].value), ntohl(d[i+1].value), ntohl(d[i+2].value));
+	    i+=3;
+
+	    i = unwrap_string(d, i+1, &data, &len);
+	    
+	    printf("           ({%d}%s)\n", len, (!data ? "[nil]" : data));
+	    break;
+	    
+	case B_NOTIFY: /*13*/
+	    i = unwrap_string(d, i+1, &data, &len);
+
+	    printf("%d: NOTIFY METHOD({%d}%s)\n",i,len,data);
+
+	    i = unwrap_string(d, i, &data, &len);
+
+	    printf("            ID({%d}%s) OPTIONS ", len,
+		   (!data ? "[nil]" : data));
+
+	    i=write_list(ntohl(d[i].len),i+1,d);
+	    
+	    printf("            PRIORITY(%d)\n", ntohl(d[i].value));
+      	    i++;
+		  
+	    i = unwrap_string(d, i, &data, &len);
+
+	    printf("            MESSAGE({%d}%s)\n", len, data);
+
+	    break;
+
+	case B_VACATION:/*14*/
+	    printf("%d: VACATION\n",i);
+	    /*add address list here!*/
+	    i=write_list(ntohl(d[i+1].len),i+2,d);
+
+	    i = unwrap_string(d, i, &data, &len);
+	  
+	    printf("%d SUBJ({%d}%s) \n",i, len, (!data ? "[nil]" : data));
+	    
+	    i = unwrap_string(d, i, &data, &len);
+
+	    printf("%d MESG({%d}%s) \n", i, len, (!data ? "[nil]" : data));
+
+	    printf("DAYS(%d) MIME(%d)\n", ntohl(d[i].value), ntohl(d[i+1].value));
+	    i+=2;
+
+	    break;
+	case B_NULL:/*15*/
+	    printf("%d:NULL\n",i);
+	    i++;
+	    break;
+	case B_JUMP:/*16*/
+	    printf("%d:JUMP %d\n",i, ntohl(d[i+1].jump));
+	    i+=2;
+	    break;		  
+	default:
+	    printf("%d: %d (NOT AN OP)\n",i,ntohl(d[i].op));
+	    exit(1);
+	}
+    }
+    printf("full len is: %d\n", bc_len);
+}
+
+
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/interp.h
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/interp.h
@@ -0,0 +1,56 @@
+/* interp.h -- interpretor definition
+ * Larry Greenfield
+ * $Id$
+ */
+/***********************************************************
+        Copyright 1999 by Carnegie Mellon University
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*****************************************************************/
+
+#ifndef SIEVE_INTERP_H
+#define SIEVE_INTERP_H
+
+#include "sieve_interface.h"
+
+struct sieve_interp {
+    /* standard callbacks for actions */
+    sieve_callback *redirect, *discard, *reject, *fileinto, *keep;
+    sieve_callback *notify;
+    sieve_vacation_t *vacation;
+
+    sieve_get_size *getsize;
+    sieve_get_header *getheader;
+    sieve_get_envelope *getenvelope;
+
+    sieve_parse_error *err;
+
+    /* site-specific imapflags for mark/unmark */
+    sieve_imapflags_t *markflags;
+
+    sieve_execute_error *execute_err;
+
+    /* context to pass along */
+    void *interp_context;
+};
+
+int interp_verify(sieve_interp_t *interp);
+
+#endif
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/tree.h
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/tree.h
@@ -0,0 +1,139 @@
+/* tree.h -- abstract syntax tree
+ * Larry Greenfield
+ * $Id$
+ */
+/***********************************************************
+        Copyright 1999 by Carnegie Mellon University
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+******************************************************************/
+
+#ifndef TREE_H
+#define TREE_H
+
+#include "comparator.h"
+
+/* abstract syntax tree for sieve */
+typedef struct Stringlist stringlist_t;
+typedef struct Commandlist commandlist_t;
+typedef struct Test test_t;
+typedef struct Testlist testlist_t;
+typedef struct Tag tag_t;
+typedef struct Taglist taglist_t;
+
+struct Stringlist {
+    char *s;
+    stringlist_t *next;
+};
+
+ 
+struct Tag {
+    int type;
+    char *arg;
+};
+
+struct Taglist {
+    tag_t *t;
+    taglist_t *next;
+};
+
+struct Test {
+    int type;
+    union {
+	testlist_t *tl; /* anyof, allof */
+	stringlist_t *sl; /* exists */
+	struct { /* it's a header test */
+	    int comptag;
+	    char * comparator;
+	    int relation;
+	    void *comprock;
+	    stringlist_t *sl;
+	    stringlist_t *pl;
+	} h;
+	struct { /* it's an address or envelope test */
+	    int comptag;
+	    char * comparator;
+	    int relation; 
+	    void *comprock;
+	    stringlist_t *sl;
+	    stringlist_t *pl;
+            int addrpart;
+	} ae; 
+	test_t *t; /* not */
+	struct { /* size */
+	    int t; /* tag */
+	    int n; /* param */
+	} sz;
+    } u;
+};
+
+struct Testlist {
+    test_t *t;
+    testlist_t *next;
+};
+
+struct Commandlist {
+    int type;
+    union {
+        char *str;
+	stringlist_t *sl; /* the parameters */
+	struct { /* it's an if statement */
+	    test_t *t;
+	    commandlist_t *do_then;
+	    commandlist_t *do_else;
+	} i;
+	struct { /* it's a vacation action */
+	    char *subject;
+	    int days;
+	    stringlist_t *addresses;
+	    char *message;
+	    int mime;
+	} v;
+	struct { /* it's a notify action */
+	    char *method;
+	    char *id;
+	    stringlist_t *options;
+	    int priority;
+	    char *message;
+	} n;
+	struct { /* it's a denotify action */
+	    int comptag;
+	    int relation;
+	    void *comprock;
+	    void *pattern;
+	    int priority;
+	} d;
+    } u;
+    struct Commandlist *next;
+};
+
+stringlist_t *new_sl(char *s, stringlist_t *n);
+tag_t *new_tag(int type, char *s);
+taglist_t *new_taglist(tag_t *t, taglist_t *n);
+test_t *new_test(int type);
+testlist_t *new_testlist(test_t *t, testlist_t *n);
+commandlist_t *new_command(int type);
+commandlist_t *new_if(test_t *t, commandlist_t *y, commandlist_t *n);
+
+void free_sl(stringlist_t *sl);
+void free_test(test_t *t);
+void free_tree(commandlist_t *cl);
+
+#endif
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/Makefile.am
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/Makefile.am
@@ -0,0 +1,80 @@
+pkglibexecdir = $(libexecdir)/dovecot
+
+noinst_LTLIBRARIES = libsieve.la
+
+AM_YFLAGS = -d -p $*
+
+AM_CPPFLAGS = \
+	-I$(dovecot_incdir) \
+	-I$(dovecot_incdir)/src/lib \
+	-I$(top_srcdir)/src
+
+addr-lex.c: addr-lex.l
+	$(LEX) -t -Paddr addr-lex.l > addr-lex.c
+
+sieve-lex.c: sieve-lex.l
+	$(LEX) -t sieve-lex.l > sieve-lex.c
+
+libsieve_la_SOURCES = \
+	addr.y \
+	sieve.y \
+	addr-lex.l \
+	sieve-lex.l \
+	bc_dump.c \
+	bc_emit.c \
+	bc_eval.c \
+	bc_generate.c \
+	comparator.c \
+	interp.c \
+	message.c \
+	parseaddr.c \
+	script.c \
+	sieve_err.c \
+	tree.c
+
+noinst_HEADERS = \
+	addr.h \
+	bytecode.h \
+	comparator.h \
+	interp.h \
+	message.h \
+	parseaddr.h \
+	script.h \
+	sieve.h \
+	sieve_err.h \
+	sieve_interface.h \
+	tree.h
+
+if HAVE_DOVECOT_LIBS
+pkglibexec_PROGRAMS = sievec sieved
+
+sievec_SOURCES = \
+	sievec.c \
+	../map.c \
+	../imparse.c
+
+sieved_SOURCES = \
+	sieved.c \
+	../map.c
+
+sievec_LDADD = \
+	libsieve.la \
+	$(dovecotdir)/src/lib/liblib.a
+
+sieved_LDADD = \
+	libsieve.la \
+	$(dovecotdir)/src/lib/liblib.a
+
+notbuilt_sources =
+else
+notbuilt_sources = sievec.c sieved.c
+endif
+
+EXTRA_DIST = \
+	addr-lex.l \
+	sieve-lex.l \
+	AUTHORS \
+	COPYING \
+	NEWS \
+	README \
+	$(notbuilt_sources)
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/NEWS
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/NEWS
@@ -0,0 +1,84 @@
+$Id$
+
+CMU Sieve 2.1
+-------------
+
+- Compliant with RFC 3028.  As a result, fileinto and redirect only
+  accept a single string and NOT a string-list.
+
+- Compliant with draft-martin-sieve-notify-01.  As a result, notify
+  actions will need to be updated to the new syntax.
+
+CMU Sieve 2.0
+-------------
+
+- Compliant with draft-showalter-sieve-11.txt and 
+  draft-showalter-sieve-vacation-03.txt.
+
+- Added support for the regex, imapflags, notify and subaddress extensions.
+  See README for references.
+
+- Verifies email addresses in redirect and vacation actions are syntactically
+  correct (compliant with RFC822).
+
+- Run-time error reporting.
+
+- Changed callback interface to use callback contexts instead of individual
+  parameters.  Also added an error string buffer for run-time error reporting.
+
+- Vacation will not reply to any message containing an "auto-submitted"
+  header containing anything other than "no".
+
+CMU Sieve 1.4
+-------------
+
+Now included with imapd distribution (hell, why not?).
+
+Error returning and recovering:
+	added error recovering to the parser (but not much!)
+	added error messages to the parser
+
+Working on error returning and error recovering.
+	run-time errors
+	detect some errors in lexer?
+
+Working on even better parsing:
+	verify addresses could be addresses
+	verify mailboxes could be mailboxes
+	verify outgoing headers can be headers
+
+CMU Sieve 1.3
+-------------
+
+Changed for integration with cyrus deliver.
+
+CMU Sieve 1.2
+-------------
+
+Added additional callbacks (ok, so I want to make my integration with deliver
+easier) and envelope and vacation support.
+
+Made it compile without libcyrus.
+It should compile without libcyrus, but then it does not implement the
+"address" test.	 That's just too much work to do when I have a neato
+library to do it for me.
+
+Todo:
+- regex matching
+
+CMU Sieve 1.1
+-------------
+
+- Updated to draft-showalter-sieve-07bis.txt
+
+- Simple API (see sieve_interface.h; currently mostly undocumented)
+
+- Implements all of the optional features except "envelope"
+
+- Maintains "if it parses, it probably runs" behavior. (Goal: minimize
+  run-time errors.)
+
+CMU Sieve 1.0
+-------------
+
+- prototype implementation
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/parseaddr.h
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/parseaddr.h
@@ -0,0 +1,69 @@
+/* parseaddr.h -- RFC 822 address parser
+ $Id$
+ 
+ * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any other legal
+ *    details, please contact  
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ *
+ */
+
+#ifndef INCLUDED_PARSEADDR_H
+#define INCLUDED_PARSEADDR_H
+
+#ifndef P
+#ifdef __STDC__
+#define P(x) x
+#else
+#define P(x) ()
+#endif
+#endif
+
+struct address {
+    char *name;
+    char *route;
+    char *mailbox;
+    char *domain;
+    struct address *next;
+    char *freeme;		/* If non-nil, free */
+};
+
+extern void parseaddr_list P((const char *s, struct address **addrp));
+extern void parseaddr_free P((struct address *addr));
+
+
+#endif /* INCLUDED_PARSEADDR_H */
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/sieve_err.c
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/sieve_err.c
@@ -0,0 +1,60 @@
+/*
+ * sieve_err.c:
+ * This file is automatically generated; please do not edit it.
+ */
+
+#include <stdlib.h>
+
+static const char * const text[] = {
+	   "Generic Sieve error",
+	   "Sieve interpretor not finalized",
+	   "Parse error in Sieve script",
+	   "Run-time error during Sieve execution",
+	   "Internal error in Sieve subsystem",
+	   "Memory exhausted in Sieve subsystem",
+	   "Sieve action already taken",
+    0
+};
+
+struct error_table {
+    char const * const * msgs;
+    long base;
+    int n_msgs;
+};
+struct et_list {
+    struct et_list *next;
+    const struct error_table * table;
+};
+extern struct et_list *_et_list;
+
+const struct error_table et_siev_error_table = { text, -1237848064L, 7 };
+
+static struct et_list link = { 0, 0 };
+
+void initialize_siev_error_table(void);
+
+void initialize_siev_error_table(void) {
+    if (!link.table) {
+        link.next = _et_list;
+        link.table = &et_siev_error_table;
+        _et_list = &link;
+    }
+}
+
+/* For Heimdal compatibility */
+void initialize_siev_error_table_r(struct et_list **list);
+
+void initialize_siev_error_table_r(struct et_list **list)
+{
+    struct et_list *et, **end;
+
+    for (end = list, et = *list; et; end = &et->next, et = et->next)
+        if (et->table->msgs == text)
+            return;
+    et = malloc(sizeof(struct et_list));
+    if (et == 0)
+        return;
+    et->table = &et_siev_error_table;
+    et->next = 0;
+    *end = et;
+}
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/bc_dump.c
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/bc_dump.c
@@ -0,0 +1,304 @@
+/* bc_generate.c -- sieve bytecode- almost flattened bytecode
+ * Rob Siemborski
+ * $Id$
+ */
+/***********************************************************
+        Copyright 2001 by Carnegie Mellon University
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+******************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+ 
+#include "sieve_interface.h"
+#include "bytecode.h"
+
+ 
+struct bytecode_info 
+{
+    bytecode_t *data;/* pointer to almost-flat bytecode */
+    size_t scriptend; /* used by emit code to know final length of bytecode */
+    size_t reallen; /* allocated length of 'data' */
+};
+
+#if DUMPCODE
+
+/*this would work a lot better if we actually could tell how many levels deep in if statements we were.  currently it doesn't know*/
+
+static void print_spaces(int n)
+{
+    int temp_n=0;
+    while(temp_n++ < (n))
+	putchar(' ');
+}
+
+
+/* Dump a stringlist.  Return the last address used by the list */
+static int dump_sl(bytecode_info_t *d, int ip, int level) 
+{
+    int numstr = d->data[ip].listlen;
+    int i;
+    
+    for(i=0; i<numstr; i++) {
+	print_spaces(level*4);
+	printf(" {%d}",d->data[++ip].len);
+	printf("%s\n",d->data[++ip].str);
+    }
+    
+    return ip;
+}
+
+static int dump_test(bytecode_info_t *d, int ip, int level);
+
+/* Dump a testlist.  Return the last address used by the list */
+static int dump_tl(bytecode_info_t *d, int ip, int level) 
+{
+    int numtest = d->data[ip].listlen;
+    int i;
+    
+    for(i=0; i<numtest; i++) {
+	print_spaces(level*4);
+	printf(" (until %d)\n", d->data[++ip].jump);
+	ip = dump_test(d, ++ip, level);
+    }
+    
+    return ip;
+}
+
+/* Dump a test, return the last address used by the test */
+static int dump_test(bytecode_info_t *d, int ip, int level ) {
+
+    print_spaces(level*4);
+    switch(d->data[ip].op) {
+    case BC_TRUE:
+	printf("%d: TRUE\n",ip);
+	break;
+
+    case BC_FALSE:
+	printf("%d: FALSE\n",ip);
+	break;
+
+    case BC_NOT:
+	printf("%d: NOT TEST(\n",ip++);
+	/*   printf("  (until %d)\n", d->data[ip++].jump);*/
+	ip = dump_test(d,ip, level);
+	print_spaces(level*4);
+	printf("    )\n");
+	break;
+
+    case BC_SIZE:
+	printf("%d: SIZE TAG(%d) NUM(%d)\n",ip,
+	       d->data[ip+1].value, d->data[ip+2].value);
+	ip+=2;
+	break;
+
+    case BC_EXISTS:
+	printf("%d: EXISTS\n",ip++);
+	ip = dump_sl(d,ip,level);
+	break;
+
+    case BC_ALLOF:
+	printf("%d: ALLOF (\n",ip++);
+	ip = dump_tl(d,ip,level);
+	print_spaces(level*4);
+	printf(")\n");
+	break;
+
+    case BC_ANYOF:
+	printf("%d: ANYOF (\n",ip++);
+	ip = dump_tl(d,ip, level);
+	  print_spaces(level*4);
+	printf(")\n");
+	break;
+	    
+    case BC_HEADER:
+	printf("%d: HEADER (\n",ip++);
+	print_spaces(level*4);
+	if (d->data[ip].value == B_COUNT || d->data[ip].value == B_VALUE)
+	{
+	    printf("      MATCH:%d  RELATION:%d  COMP:%d HEADERS:\n", 
+		   d->data[ip].value, d->data[ip+1].value,d->data[ip+2].value);
+	} else {
+	    printf("      MATCH:%d COMP:%d HEADERS:\n",d->data[ip].value, d->data[ip+2].value);
+	}
+	ip+=3;
+	ip = dump_sl(d,ip,level);
+	ip++;
+	print_spaces(level*4);
+	printf("      DATA:\n");
+	ip = dump_sl(d,ip,level);
+	break;
+	
+    case BC_ADDRESS:
+    case BC_ENVELOPE:
+	printf("%d: %s (\n",ip++,
+	       d->data[ip].op == BC_ADDRESS ? "ADDRESS" : "ENVELOPE");
+	print_spaces(level*4);
+	if (d->data[ip].value == B_COUNT || d->data[ip].value == B_VALUE)
+	{
+	    printf("      MATCH:%d RELATION: %d COMP: %d TYPE: %d HEADERS:\n", 
+		   d->data[ip].value, d->data[ip+1].value, d->data[ip+2].value, d->data[ip+3].value);
+	} else {
+	    printf("      MATCH:%d COMP:%d TYPE:%d HEADERS:\n",
+		   d->data[ip].value,d->data[ip+1].value,d->data[ip+3].value);
+	}
+	ip+=4;
+	ip = dump_sl(d,ip,level); ip++;
+	print_spaces(level*4);
+	printf("      DATA:\n");
+	ip = dump_sl(d,ip,level);
+	break;
+
+    default:
+	printf("%d: TEST(%d)\n",ip,d->data[ip].op);
+	break;
+    }
+
+    return ip;
+}
+
+void dump(bytecode_info_t *d, int level) 
+{
+    int i;
+    printf("Dumping almost flattened bytecode\n\n");
+    
+    if(!d) return;
+    
+    for(i=0; i<d->scriptend; i++) {
+	print_spaces(level*4);
+	switch(d->data[i].op) {
+	case B_REJECT:
+	    printf("%d: REJECT {%d}%s\n",i,
+		   d->data[i+1].len,d->data[i+2].str);
+	    i+=2;
+	    break;
+	case B_IF:
+	    if (d->data[i+3].jump== -1)
+	    {
+		printf("%d: IF THEN(%d) POST(%d) TEST(\n",i,
+		       d->data[i+1].jump,d->data[i+2].jump);
+	    }
+	    else
+	    {
+		printf("%d: IF THEN(%d) ELSE(%d) POST(%d) TEST(\n",i,
+		       d->data[i+1].jump,d->data[i+2].jump,
+		       d->data[i+3].jump);
+	    }
+	    i = dump_test(d,i+4, level+1);
+	    printf(")\n");
+	    break;
+
+	case B_STOP:
+	    printf("%d: STOP\n",i);
+	    break;
+
+	case B_DISCARD:
+	    printf("%d: DISCARD\n",i);
+	    break;
+	    
+	case B_KEEP:
+	    printf("%d: KEEP\n",i);
+	    break;
+
+	case B_MARK:
+	    printf("%d: MARK\n",i);
+	    break;
+
+	case B_UNMARK:
+	    printf("%d: UNMARK\n",i);
+	    break;
+
+	case B_FILEINTO:
+	    printf("%d: FILEINTO {%d}%s\n",i,
+		   d->data[i+1].len,d->data[i+2].str);
+	    i+=2;
+	    break;
+
+	case B_REDIRECT:
+	    printf("%d: REDIRECT {%d}%s\n",i,
+		   d->data[i+1].len,d->data[i+2].str);
+	    i+=2;
+	    break;
+
+	case B_SETFLAG:
+	    printf("%d: SETFLAG\n",i);
+	    i=dump_sl(d,++i, level);
+	    break;
+
+	case B_ADDFLAG:
+	    printf("%d: ADDFLAG\n",i);
+	    i=dump_sl(d,++i,level);
+	    break;
+
+	case B_REMOVEFLAG:
+	    printf("%d: REMOVEFLAG\n",i);
+	    i=dump_sl(d,++i,level);
+	    break;
+
+	case B_DENOTIFY:
+	    printf("%d: DENOTIFY priority %d,comp %d %d  %s\n", 
+		   i,
+		   d->data[i+1].value,
+		   d->data[i+2].value,
+		   d->data[i+3].value,
+		   (d->data[i+4].len == -1 ? "[nil]" : d->data[i+5].str));
+	    i+=5;
+	    break;
+
+	case B_NOTIFY: 
+	    printf("%d: NOTIFY\n   METHOD(%s),\n   ID(%s),\n   OPTIONS",
+		   i,
+		   d->data[i+2].str,
+		   (d->data[i+3].len == -1 ? "[nil]" : d->data[i+4].str));
+	    i+=5;
+	    i=dump_sl(d,i,level);
+	    printf("   PRIORITY(%d),\n   MESSAGE({%d}%s)\n", 
+		   d->data[i+1].value, d->data[i+2].len,d->data[i+3].str);
+	    i+=3;
+	    break;
+
+	case B_VACATION:
+	    printf("%d:VACATION\n",i);
+	    i++;
+	    i=dump_sl(d,i,level);
+	    printf("SUBJ({%d}%s) MESG({%d}%s)\n DAYS(%d) MIME(%d)\n", 
+		   d->data[i+1].len, (d->data[i+1].len == -1 ? "[nil]" : d->data[i+2].str),
+		   d->data[i+3].len, (d->data[i+3].len == -1 ? "[nil]" : d->data[i+4].str),
+		   d->data[i+5].value, d->data[i+6].value);
+	    i+=6;
+	
+	    break;
+	case B_JUMP:
+	    printf("%d: JUMP HUH?  this shouldn't be here>?!",i);
+	    break;
+	case B_NULL:
+	    printf("%d: NULL\n",i);
+	    break;
+	default:
+	    printf("%d: %d\n",i,d->data[i].op);
+	    break;
+	}
+    }
+    printf("full len is: %d\n", d->scriptend);
+}
+#endif
+
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/comparator.h
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/comparator.h
@@ -0,0 +1,48 @@
+/* comparator.h
+ * Larry Greenfield
+ * $Id$
+ */
+/***********************************************************
+        Copyright 1999 by Carnegie Mellon University
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+******************************************************************/
+
+#ifndef COMPARATOR_H
+#define COMPARATOR_H
+
+#ifdef ENABLE_REGEX
+#ifdef HAVE_RX
+#include <rxposix.h>
+#else
+#include <sys/types.h>
+#include <regex.h>
+#endif
+#endif
+
+/* compares pat to text; returns 1 if it's true, 0 otherwise 
+   first arg is text, second arg is pat, third arg is rock */
+typedef int comparator_t(const char *, const char *, void *);
+
+/* returns a pointer to a comparator function given it's name */
+comparator_t *lookup_comp(int comp, int mode,
+			  int relation, void **rock);
+
+#endif
--- dovecot-1.0.15.orig/dovecot-sieve/src/libsieve/sieve-lex.l
+++ dovecot-1.0.15/dovecot-sieve/src/libsieve/sieve-lex.l
@@ -0,0 +1,162 @@
+%{
+/* sieve.l -- sieve lexer
+ * Larry Greenfield
+ * $Id$
+ */
+/***********************************************************
+        Copyright 1999 by Carnegie Mellon University
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+******************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h> /* for strdup */
+#include "xmalloc.h"
+
+#include "tree.h"
+#include "sieve.h"
+
+#define yylval sievelval
+#define yylex sievelex
+#define yyerror sieveerror
+
+static int tonum(char *c);
+static char *chkstr(char *);
+static char *mlbuf;
+static int mlbufsz, mlcur;
+extern int yyerror(char *);
+%}
+
+%option yylineno
+%option noyywrap
+%option nounput
+
+ws		[ \t]+
+ident		[a-zA-Z_][a-zA-Z_0-9]*
+CRLF		(\r\n|\r|\n)
+
+%state MULTILINE
+%state QSTRING
+
+%%
+<MULTILINE>^\.{CRLF}	{ BEGIN INITIAL; 
+                          if (mlbuf) mlbuf[mlcur] = '\0';
+                          yylval.sval = chkstr(mlbuf); return STRING; }
+<MULTILINE>^\.\.  { /* dot stuffing! we want one . */ yyless(1); }
+<MULTILINE>(.|\n) { if (mlcur == mlbufsz) 
+			mlbuf = xrealloc(mlbuf, 1 + (mlbufsz+=1024));
+		    mlbuf[mlcur++] = yytext[0]; }
+<MULTILINE><<EOF>> { yyerror("unexpected end of file in string"); 
+		     yyterminate(); }
+<QSTRING>\"        { BEGIN INITIAL;
+                     if (mlbuf) mlbuf[mlcur] = '\0';
+		     yylval.sval = chkstr(mlbuf); return STRING; }
+<QSTRING>\\.      { if (mlcur == mlbufsz) 
+			mlbuf = xrealloc(mlbuf, 1 + (mlbufsz+=1024));
+		    mlbuf[mlcur++] = yytext[1]; }
+<QSTRING>(.|\n)   { if (mlcur == mlbufsz) 
+			mlbuf = xrealloc(mlbuf, 1 + (mlbufsz+=1024));
+		    mlbuf[mlcur++] = yytext[0]; }
+<INITIAL>text:{ws}?(#.*)?{CRLF}	{ BEGIN MULTILINE;
+			  mlcur = 0; mlbufsz = 0; mlbuf = NULL; }
+<INITIAL>\"        { BEGIN QSTRING;
+                    mlcur = 0; mlbufsz = 0; mlbuf = NULL; }
+<INITIAL>[0-9]+[KMG]?	{ yylval.nval = tonum(yytext); return NUMBER; }
+<INITIAL>if		return IF;
+<INITIAL>elsif		return ELSIF;
+<INITIAL>else		return ELSE;
+<INITIAL>anyof		return ANYOF;
+<INITIAL>allof		return ALLOF;
+<INITIAL>exists		return EXISTS;
+<INITIAL>false		return SFALSE;
+<INITIAL>true		return STRUE;
+<INITIAL>address	return ADDRESS;
+<INITIAL>envelope	return ENVELOPE;
+<INITIAL>header		return HEADER;
+<INITIAL>not		return NOT;
+<INITIAL>size		return SIZE;
+<INITIAL>reject		return REJCT;
+<INITIAL>fileinto	return FILEINTO;
+<INITIAL>redirect	return REDIRECT;
+<INITIAL>keep		return KEEP;
+<INITIAL>require	return REQUIRE;
+<INITIAL>stop		return STOP;
+<INITIAL>discard	return DISCARD;
+<INITIAL>setflag	return SETFLAG;
+<INITIAL>addflag	return ADDFLAG;
+<INITIAL>removeflag	return REMOVEFLAG;
+<INITIAL>mark		return MARK;
+<INITIAL>unmark		return UNMARK;
+<INITIAL>notify		return NOTIFY;
+<INITIAL>denotify	return DENOTIFY;
+<INITIAL>:id		return ID;
+<INITIAL>:method	return METHOD;
+<INITIAL>:options	return OPTIONS;
+<INITIAL>:low		return LOW;
+<INITIAL>:normal	return NORMAL;
+<INITIAL>:high		return HIGH;
+<INITIAL>:message	return MESSAGE;
+<INITIAL>vacation	return VACATION;
+<INITIAL>:days		return DAYS;
+<INITIAL>:addresses	return ADDRESSES;
+<INITIAL>:subject	return SUBJECT;
+<INITIAL>:mime		return MIME;
+<INITIAL>:comparator	return COMPARATOR;
+<INITIAL>:is		return IS;
+<INITIAL>:contains	return CONTAINS;
+<INITIAL>:matches	return MATCHES;
+<INITIAL>:regex		return REGEX;
+<INITIAL>:count		return COUNT;
+<INITIAL>:value		return VALUE;
+<INITIAL>:over		return OVER;
+<INITIAL>:under		return UNDER;
+<INITIAL>:all		return ALL;
+<INITIAL>:localpart	return LOCALPART;
+<INITIAL>:domain	return DOMAIN;
+<INITIAL>:user		return USER;
+<INITIAL>:detail	return DETAIL;
+<INITIAL>[ \t\n\r] ;	/* ignore whitespace */
+<INITIAL>#.* ;		/* ignore hash comments */
+<INITIAL>"/*"([^\*]|\*[^\/])*\*?"*/" ;	/* ignore bracket comments */
+.			return yytext[0];
+
+%%
+/*  */
+static int tonum(char *c)
+{
+  int val = atoi(c);
+  switch (c[strlen(c)-1]) {
+  case 'K': val *= (1 << 10); break;
+  case 'M': val *= (1 << 20); break;
+  case 'G': val *= (1 << 30); break;
+  default: break;
+  }
+  return val;
+}
+
+/* convert NULL strings to "" */
+static char *chkstr(char *str)
+{
+    if (!str) return xstrdup("");
+    else return str;
+}
--- dovecot-1.0.15.orig/dovecot-sieve/src/xmalloc.h
+++ dovecot-1.0.15/dovecot-sieve/src/xmalloc.h
@@ -0,0 +1,26 @@
+#ifndef __XMALLOC_H
+#define __XMALLOC_H
+
+#include <stdlib.h>
+#include <string.h>
+
+#define xmalloc(n) malloc(n)
+#define xrealloc(n, m) realloc(n, m)
+#define xzmalloc(n) calloc(n, 1)
+#define xstrdup(s) strdup(s)
+
+/* missing headers.. */
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <regex.h>
+#include <fcntl.h>
+
+/* dovecot kludges */
+#include "lib.h"
+
+/* we don't have strlcpy, but strocpy is the same except for return value */
+#define strlcpy strocpy
+
+#define lcase str_lcase
+
+#endif
--- dovecot-1.0.15.orig/dovecot-sieve/INSTALL
+++ dovecot-1.0.15/dovecot-sieve/INSTALL
@@ -0,0 +1,25 @@
+Compiling
+---------
+
+Use --with-dovecot=<path> to point to dovecot-config file's directory.
+There are two possibilities where this could exist:
+
+1) If you configured Dovecot with --enable-header-install, you'll have
+dovecot-config installed in $prefix/lib/dovecot/ directory.
+
+2) Compiled Dovecot sources' root directory.
+
+So for example:
+
+./configure --with-dovecot=/usr/local/lib/dovecot
+make
+sudo make install
+
+sievec and sieved binaries are built only if you use 2) method, because
+they need to link with Dovecot's libraries. They can be used to compile and
+decompile Sieve scripts. You probably don't need these.
+
+Configuring
+-----------
+
+See the wiki page: http://wiki.dovecot.org/LDA/Sieve
--- dovecot-1.0.15.orig/dovecot-sieve/COPYING.LGPL
+++ dovecot-1.0.15/dovecot-sieve/COPYING.LGPL
@@ -0,0 +1,510 @@
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+^L
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it
+becomes a de-facto standard.  To achieve this, non-free programs must
+be allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+^L
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control
+compilation and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+^L
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+^L
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at least
+    three years, to give the same user the materials specified in
+    Subsection 6a, above, for a charge no more than the cost of
+    performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+^L
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+^L
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply, and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License
+may add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+^L
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+^L
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms
+of the ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.
+It is safest to attach them to the start of each source file to most
+effectively convey the exclusion of warranty; and each file should
+have at least the "copyright" line and a pointer to where the full
+notice is found.
+
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or
+your school, if any, to sign a "copyright disclaimer" for the library,
+if necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James
+  Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
--- dovecot-1.0.15.orig/dovecot-sieve/Makefile.am
+++ dovecot-1.0.15/dovecot-sieve/Makefile.am
@@ -0,0 +1,10 @@
+SUBDIRS = src
+
+EXTRA_DIST = \
+	COPYING.LGPL \
+	ChangeLog
+
+if MAINTAINER_MODE
+ChangeLog: .hg/dirstate
+	hg log --style=changelog > ChangeLog
+endif
--- dovecot-1.0.15.orig/dovecot-sieve/NEWS
+++ dovecot-1.0.15/dovecot-sieve/NEWS
@@ -0,0 +1,3 @@
+v1.0 2006-11-05  Timo Sirainen <tss@iki.fi>
+
+	* The first actual release
--- dovecot-1.0.15.orig/debian/dovecot-common.prerm
+++ dovecot-1.0.15/debian/dovecot-common.prerm
@@ -0,0 +1,4 @@
+#!/bin/sh
+set -e
+
+#DEBHELPER#
--- dovecot-1.0.15.orig/debian/maildirmake.dovecot
+++ dovecot-1.0.15/debian/maildirmake.dovecot
@@ -0,0 +1,28 @@
+#!/bin/sh
+#
+# maildirmake.dovecot -- create maildirs
+# Copyright (c) 2003, Jaldhar H. Vyas
+# "Do what thou wilt" shall be the whole of the license.
+#
+dir="$1"
+owner="$2"
+if [ -z "$dir" ]; then
+  echo "Must supply a directory path"
+  exit 1
+fi 
+
+if [ "$dir" = "-h" ]; then
+  echo "usage: $0 directory [user]"
+  exit 0
+fi 
+
+umask 077
+mkdir -p "$dir/cur" "$dir/new" "$dir/tmp" || exit 1
+chmod 0700 "$dir" "$dir/cur" "$dir/new" "$dir/tmp" || exit 1
+
+if [ -n "$owner" ]; then 
+  chown -R "$owner" "$dir" || exit 1
+fi
+
+exit 0
+
--- dovecot-1.0.15.orig/debian/dovecot-imapd.postinst
+++ dovecot-1.0.15/debian/dovecot-imapd.postinst
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+set -e
+
+if [ "$1" = "configure" -a -z "$2" ]; then
+	# Add the imap and imaps options to the protocols line on first install
+	perl -pi.bak -e 'if (/^\s*protocols =/i) { s/none//; s/$/ imap imaps/ unless /imap/; s/[ \t]+/ /g; };'\
+   /etc/dovecot/dovecot.conf
+fi
+
+if [ -x "/etc/init.d/dovecot" ]; then
+	if [ -x /usr/sbin/invoke-rc.d ]; then
+		invoke-rc.d dovecot restart
+	else
+		/etc/init.d/dovecot restart
+	fi
+fi
+
+#DEBHELPER#
--- dovecot-1.0.15.orig/debian/dovecot-imapd.postrm
+++ dovecot-1.0.15/debian/dovecot-imapd.postrm
@@ -0,0 +1,20 @@
+#!/bin/sh
+set -e
+
+
+if [ "$1" = "remove" ]; then
+	# Remove the imaps and imap option from the protocols line
+	perl -pi.bak -e 'if (/^\s*protocols =/i) { s/imaps//; s/imap//; s/$/ none/ unless (/pop3/ or /none/); s/[ \t]+/ /g; };'\
+   /etc/dovecot/dovecot.conf
+fi
+
+# Restart dovecot because we've updated the configuration file.
+if [ -x "/etc/init.d/dovecot" ]; then
+	if [ -x /usr/sbin/invoke-rc.d ] ; then
+		invoke-rc.d dovecot start
+	else
+		/etc/init.d/dovecot start
+	fi
+fi
+
+#DEBHELPER#
--- dovecot-1.0.15.orig/debian/dovecot-common.preinst
+++ dovecot-1.0.15/debian/dovecot-common.preinst
@@ -0,0 +1,24 @@
+#!/bin/sh
+set -e
+
+if [ ! -e /etc/dovecot ];
+then
+  mkdir /etc/dovecot
+fi
+
+for i in dovecot.conf dovecot-ldap.conf dovecot-mysql.conf dovecot-pgsql.conf;
+do
+  if [ -f /etc/$i ];
+  then
+    mv /etc/$i /etc/dovecot/$i
+    for j in dpkg-dist dpkg-new dpkg-old bak;
+    do
+      if [ -f /etc/$i.$j ];
+      then
+        mv /etc/$i.$j /etc/dovecot/$i.$j
+      fi
+    done
+  fi
+done
+
+#DEBHELPER#
--- dovecot-1.0.15.orig/debian/dovecot-common.docs
+++ dovecot-1.0.15/debian/dovecot-common.docs
@@ -0,0 +1,3 @@
+NEWS
+README
+TODO
--- dovecot-1.0.15.orig/debian/patches/dovecot-ssl.dpatch
+++ dovecot-1.0.15/debian/patches/dovecot-ssl.dpatch
@@ -0,0 +1,207 @@
+#! /bin/sh -e
+
+. $(dirname $0)/DPATCH
+
+exit 0
+@DPATCH@
+--- dovecot-1.0.5.orig/src/login-common/ssl-proxy.c
++++ dovecot-1.0.5/src/login-common/ssl-proxy.c
+@@ -33,6 +33,11 @@
+ 
+ void ssl_proxy_free(struct ssl_proxy *proxy __attr_unused__) {}
+ 
++const char *ssl_proxy_get_fingerprint(struct ssl_proxy *proxy __attr_unused__)
++{
++    return NULL;
++}
++
+ unsigned int ssl_proxy_get_count(void)
+ {
+ 	return 0;
+only in patch2:
+unchanged:
+--- dovecot-1.0.5.orig/src/login-common/sasl-server.c
++++ dovecot-1.0.5/src/login-common/sasl-server.c
+@@ -147,6 +147,8 @@
+ 	info.service = service;
+ 	info.cert_username = client->proxy == NULL ? NULL :
+ 		ssl_proxy_get_peer_name(client->proxy);
++	info.cert_fingerprint = client->proxy == NULL ? NULL :
++		ssl_proxy_get_fingerprint(client->proxy);
+ 	info.flags = client_get_auth_flags(client);
+ 	info.local_ip = client->local_ip;
+ 	info.remote_ip = client->ip;
+only in patch2:
+unchanged:
+--- dovecot-1.0.5.orig/src/login-common/ssl-proxy.h
++++ dovecot-1.0.5/src/login-common/ssl-proxy.h
+@@ -14,6 +14,7 @@
+ const char *ssl_proxy_get_peer_name(struct ssl_proxy *proxy);
+ bool ssl_proxy_is_handshaked(struct ssl_proxy *proxy);
+ void ssl_proxy_free(struct ssl_proxy *proxy);
++const char *ssl_proxy_get_fingerprint(struct ssl_proxy *proxy);
+ 
+ /* Return number of active SSL proxies */
+ unsigned int ssl_proxy_get_count(void);
+only in patch2:
+unchanged:
+--- dovecot-1.0.5.orig/src/login-common/ssl-proxy-openssl.c
++++ dovecot-1.0.5/src/login-common/ssl-proxy-openssl.c
+@@ -26,6 +26,8 @@
+ /* Check every 30 minutes if parameters file has been updated */
+ #define SSL_PARAMFILE_CHECK_INTERVAL (60*30)
+ 
++static const char hexcodes[] = "0123456789ABCDEF";
++
+ enum ssl_io_action {
+ 	SSL_ADD_INPUT,
+ 	SSL_REMOVE_INPUT,
+@@ -535,6 +537,35 @@
+ 	return *name == '\0' ? NULL : name;
+ }
+ 
++const char *ssl_proxy_get_fingerprint(struct ssl_proxy *proxy) {
++	X509 *x509;
++	char *peer_fingerprint = NULL;
++	unsigned char md[EVP_MAX_MD_SIZE];
++	unsigned int n;
++	int j;
++
++	if (!ssl_proxy_has_valid_client_cert(proxy))
++		return NULL;
++
++	x509 = SSL_get_peer_certificate(proxy->ssl);
++	if (x509 == NULL)
++		return NULL; /* we should have had it.. */
++
++	if (X509_digest(x509, EVP_md5(), md, &n) && n > 0) {
++		peer_fingerprint = i_malloc(n * 3);
++		for (j = 0; j < (int) n; j++) {
++			peer_fingerprint[j * 3] = hexcodes[(md[j] & 0xf0) >> 4U];
++			peer_fingerprint[(j * 3) + 1] = hexcodes[(md[j] & 0x0f)];
++			if (j + 1 != (int) n)
++				peer_fingerprint[(j * 3) + 2] = ':';
++			else
++				peer_fingerprint[(j * 3) + 2] = '\0';
++		}
++		i_info("fingerprint: %s", peer_fingerprint);
++	}
++	return (const char *)peer_fingerprint;
++}
++
+ bool ssl_proxy_is_handshaked(struct ssl_proxy *proxy)
+ {
+ 	return proxy->handshaked;
+only in patch2:
+unchanged:
+--- dovecot-1.0.5.orig/src/lib-auth/auth-server-request.c
++++ dovecot-1.0.5/src/lib-auth/auth-server-request.c
+@@ -15,7 +15,7 @@
+ 
+ 	unsigned int id;
+ 
+-	char *mech, *service, *cert_username;
++	char *mech, *service, *cert_username, *cert_fingerprint;
+         enum auth_request_flags flags;
+ 	struct ip_addr local_ip, remote_ip;
+ 
+@@ -108,6 +108,15 @@
+ 		}
+ 		str_printfa(str, "\tcert_username=%s", request->cert_username);
+ 	}
++	if (request->cert_fingerprint != NULL) {
++		if (!is_valid_string(request->cert_fingerprint)) {
++			t_pop();
++			*error_r = "Invalid fingerprint in SSL certificate";
++			return -1;
++		}
++		str_printfa(str, "\tcert_fingerprint=%s", request->cert_fingerprint);
++	}
++
+ 	if (request->local_ip.family != 0)
+ 		str_printfa(str, "\tlip=%s", net_ip2addr(&request->local_ip));
+ 	if (request->remote_ip.family != 0)
+@@ -345,6 +354,7 @@
+ 	request->mech = i_strdup(request_info->mech);
+ 	request->service = i_strdup(request_info->service);
+ 	request->cert_username = i_strdup(request_info->cert_username);
++	request->cert_fingerprint = i_strdup(request_info->cert_fingerprint);
+ 	request->flags = request_info->flags;
+ 	request->local_ip = request_info->local_ip;
+ 	request->remote_ip = request_info->remote_ip;
+@@ -385,6 +395,7 @@
+ 	i_free(request->mech);
+ 	i_free(request->service);
+ 	i_free(request->cert_username);
++	i_free(request->cert_fingerprint);
+ 	i_free(request);
+ }
+ 
+only in patch2:
+unchanged:
+--- dovecot-1.0.5.orig/src/lib-auth/auth-client.h
++++ dovecot-1.0.5/src/lib-auth/auth-client.h
+@@ -26,6 +26,7 @@
+ 	const char *mech;
+ 	const char *service;
+ 	const char *cert_username;
++	const char *cert_fingerprint;
+ 	enum auth_request_flags flags;
+ 
+ 	struct ip_addr local_ip, remote_ip;
+only in patch2:
+unchanged:
+--- dovecot-1.0.5.orig/src/auth/auth-request.c
++++ dovecot-1.0.5/src/auth/auth-request.c
+@@ -146,6 +146,8 @@
+ 		request->user = p_strdup(request->pool, value);
+ 	else if (strcmp(key, "master_user") == 0)
+ 		request->master_user = p_strdup(request->pool, value);
++	else if (strcmp(key, "cert_fingerprint") == 0)
++		request->cert_fingerprint = p_strdup(request->pool, value);
+ 	else if (strcmp(key, "cert_username") == 0) {
+ 		if (request->auth->ssl_username_from_cert) {
+ 			/* get username from SSL certificate. it overrides
+only in patch2:
+unchanged:
+--- dovecot-1.0.5.orig/src/auth/passdb-checkpassword.c
++++ dovecot-1.0.5/src/auth/passdb-checkpassword.c
+@@ -250,6 +250,10 @@
+ 			env_put(t_strconcat("MASTER_USER=",
+ 					    request->master_user, NULL));
+ 		}
++		if (request->cert_fingerprint != NULL) {
++			env_put(t_strconcat("SSL_FINGERPRINT=",
++					    request->cert_fingerprint, NULL));
++		}
+ 		if (request->extra_fields != NULL) {
+ 			const char *fields =
+ 				auth_stream_reply_export(request->extra_fields);
+only in patch2:
+unchanged:
+--- dovecot-1.0.5.orig/src/auth/auth-request.h
++++ dovecot-1.0.5/src/auth/auth-request.h
+@@ -38,6 +38,7 @@
+ 	const char *original_username;
+ 	char *mech_password; /* set if verify_plain() is called */
+ 	char *passdb_password; /* set after password lookup if successful */
++	char *cert_fingerprint;
+         /* extra_fields are returned in authentication reply. Fields prefixed
+            with "userdb_" are skipped. If prefetch userdb is used, it uses
+            the "userdb_" prefixed fields. */
+only in patch2:
+unchanged:
+--- dovecot-1.0.5.orig/src/login-common/ssl-proxy-gnutls.c
++++ dovecot-1.0.5/src/login-common/ssl-proxy-gnutls.c
+@@ -61,6 +61,11 @@
+ 	return gnutls_alert_get_name(gnutls_alert_get(proxy->session));
+ }
+ 
++const char *ssl_proxy_get_fingerprint(struct ssl_proxy *proxy __attr_unused__)
++{
++    return NULL;
++}
++
+ static int handle_ssl_error(struct ssl_proxy *proxy, int error)
+ {
+ 	if (!gnutls_error_is_fatal(error)) {
--- dovecot-1.0.15.orig/debian/patches/dovecot-sql.dpatch
+++ dovecot-1.0.15/debian/patches/dovecot-sql.dpatch
@@ -0,0 +1,21 @@
+#! /bin/sh -e
+
+## DP: Small cosmetic changes for dovecot-sql.conf
+## DP: Author: Jaldhar H. Vyas <jaldhar@debian.org>
+
+. $(dirname $0)/DPATCH
+
+exit 0
+@DPATCH@
+diff -urN dovecot-0.99.20050712/doc/dovecot-sql.conf* >> dovecot-0.99.20050712/debian/patches/dovecot-sql.dpatch
+--- dovecot-0.99.20050712/doc/dovecot-sql-example.conf	2005-06-05 21:45:25.000000000 +0000
++++ dovecot-0.99.20050712/doc/dovecot-sql-example.conf.debian	2005-07-13 12:54:23.188829784 +0000
+@@ -39,7 +39,7 @@
+ #     ssl_cert, ssl_key   - For sending client-side certificates to server
+ #     ssl_cipher          - Set minimum allowed cipher security (default: HIGH)
+ # 
+-#   You can connect to UNIX sockets by using host: host=/var/run/mysql.sock
++#   You can connect to UNIX sockets by using host: host=/var/run/mysqld/mysqld.sock
+ #   Note that currently you can't use spaces in parameters.
+ #
+ # Examples:
--- dovecot-1.0.15.orig/debian/patches/autocreate.dpatch
+++ dovecot-1.0.15/debian/patches/autocreate.dpatch
@@ -0,0 +1,141 @@
+#! /bin/sh -e
+
+## DP: Hack to support mbox snarfing, Based heavily on email found here:
+## DP: http://dovecot.org/list/dovecot/2007-May/022803.html
+## DP: Author: Timo Sirainen
+
+. $(dirname $0)/DPATCH
+
+exit 0
+@DPATCH@
+diff -uNr ./dovecot_patched/dovecot-1.0.10/configure.in ./dovecot_compile/dovecot-1.0.10/configure.in
+--- dovecot-1.0.10/configure.in	2008-02-15 15:45:32.000000000 +0100
++++ dovecot-1.0.10/configure.in	2008-02-15 11:34:32.000000000 +0100
+@@ -1888,6 +1888,7 @@
+ src/plugins/mail-log/Makefile
+ src/plugins/mbox-snarf/Makefile
+ src/plugins/trash/Makefile
++src/plugins/autocreate/Makefile
+ src/plugins/zlib/Makefile
+ stamp.h
+ dovecot-config.in])
+diff -uNr ./dovecot_patched/dovecot-1.0.10/src/plugins/autocreate/autocreate-plugin.c ./dovecot_compile/dovecot-1.0.10/src/plugins/autocreate/autocreate-plugin.c
+--- dovecot-1.0.10/src/plugins/autocreate/autocreate-plugin.c	1970-01-01 01:00:00.000000000 +0100
++++ dovecot-1.0.10/src/plugins/autocreate/autocreate-plugin.c	2008-02-14 23:42:57.000000000 +0100
+@@ -0,0 +1,68 @@
++/* Copyright (C) 2007 Timo Sirainen */
++
++/*
++   export DOVECOT=~/src/dovecot-1.0.0
++   gcc -fPIC -shared -Wall -I$DOVECOT -I$DOVECOT/src/lib \
++     -I$DOVECOT/src/lib-storage -I$DOVECOT/src/lib-mail \
++     -I$DOVECOT/src/lib-imap -DHAVE_CONFIG_H \
++     autocreate-plugin.c -o autocreate_plugin.so
++
++   Usage:
++
++   plugin {
++     autocreate = Trash
++     autocreate2 = Spam
++     #autocreate3 = ..etc..
++   }
++*/
++
++#include "lib.h"
++#include "mail-storage-private.h"
++
++#include <stdlib.h>
++
++/* defined by imap, pop3, lda */
++extern void (*hook_mail_storage_created)(struct mail_storage *storage);
++
++const char *autocreate_plugin_version = PACKAGE_VERSION;
++
++static void (*autocreate_next_hook_mail_storage_created)
++	(struct mail_storage *storage);
++
++static void autocreate_mailboxes(struct mail_storage *storage)
++{
++	char env_name[20];
++	const char *env;
++	unsigned int i;
++
++	i = 1;
++	env = getenv("AUTOCREATE");
++	while (env != NULL) {
++		(void)mail_storage_mailbox_create(storage, env, FALSE);
++		i_snprintf(env_name, sizeof(env_name), "AUTOCREATE%d", ++i);
++		env = getenv(env_name);
++	}
++}
++
++static void autocreate_mail_storage_created(struct mail_storage *storage)
++{
++	if (autocreate_next_hook_mail_storage_created != NULL)
++		autocreate_next_hook_mail_storage_created(storage);
++
++	if ((storage->flags & MAIL_STORAGE_FLAG_SHARED_NAMESPACE) == 0)
++		autocreate_mailboxes(storage);
++}
++
++void autocreate_plugin_init(void);
++void autocreate_plugin_deinit(void);
++
++void autocreate_plugin_init(void)
++{
++	autocreate_next_hook_mail_storage_created = hook_mail_storage_created;
++	hook_mail_storage_created = autocreate_mail_storage_created;
++}
++
++void autocreate_plugin_deinit(void)
++{
++	hook_mail_storage_created = autocreate_next_hook_mail_storage_created;
++}
+diff -uNr ./dovecot_patched/dovecot-1.0.10/src/plugins/autocreate/autocreate-plugin.h ./dovecot_compile/dovecot-1.0.10/src/plugins/autocreate/autocreate-plugin.h
+--- dovecot-1.0.10/src/plugins/autocreate/autocreate-plugin.h	1970-01-01 01:00:00.000000000 +0100
++++ dovecot-1.0.10/src/plugins/autocreate/autocreate-plugin.h	2008-02-14 23:42:57.000000000 +0100
+@@ -0,0 +1,7 @@
++#ifndef __AUTOCREATE_PLUGIN_H
++#define __AUTOCREATE_PLUGIN_H
++
++void autocreate_plugin_init(void);
++void autocreate_plugin_deinit(void);
++
++#endif
+diff -uNr ./dovecot_patched/dovecot-1.0.10/src/plugins/autocreate/Makefile.am ./dovecot_compile/dovecot-1.0.10/src/plugins/autocreate/Makefile.am
+--- dovecot-1.0.10/src/plugins/autocreate/Makefile.am	1970-01-01 01:00:00.000000000 +0100
++++ dovecot-1.0.10/src/plugins/autocreate/Makefile.am	2008-02-15 11:27:22.000000000 +0100
+@@ -0,0 +1,24 @@
++AM_CPPFLAGS = \
++	-I$(top_srcdir)/src/lib \
++	-I$(top_srcdir)/src/lib-mail \
++	-I$(top_srcdir)/src/lib-storage \
++	-I$(top_srcdir)/src/plugins/quota
++
++lib11_autocreate_plugin_la_LDFLAGS = -module -avoid-version
++
++module_LTLIBRARIES = \
++	lib11_autocreate_plugin.la
++
++lib11_autocreate_plugin_la_SOURCES = \
++	autocreate-plugin.c
++
++noinst_HEADERS = \
++	autocreate-plugin.h
++
++install-exec-local:
++	for d in imap pop3 lda; do \
++	  $(mkdir_p) $(DESTDIR)$(moduledir)/$$d; \
++	  rm -f $(DESTDIR)$(moduledir)/$$d/lib11_autocreate_plugin.so; \
++	  $(LN_S) ../lib11_autocreate_plugin.so $(DESTDIR)$(moduledir)/$$d; \
++	done
++
+diff -uNr ./dovecot_patched/dovecot-1.0.10/src/plugins/Makefile.am ./dovecot_compile/dovecot-1.0.10/src/plugins/Makefile.am
+--- dovecot-1.0.10/src/plugins/Makefile.am	2008-02-15 15:45:32.000000000 +0100
++++ dovecot-1.0.10/src/plugins/Makefile.am	2008-02-15 11:28:59.000000000 +0100
+@@ -2,4 +2,4 @@
+ ZLIB = zlib
+ endif
+ 
+-SUBDIRS = acl convert quota imap-quota lazy-expunge mail-log mbox-snarf trash $(ZLIB)
++SUBDIRS = acl autocreate convert quota imap-quota lazy-expunge mail-log mbox-snarf trash $(ZLIB)
--- dovecot-1.0.15.orig/debian/patches/quota_mountpoint.dpatch
+++ dovecot-1.0.15/debian/patches/quota_mountpoint.dpatch
@@ -0,0 +1,35 @@
+#! /bin/sh -e
+
+## DP: fixes missing get_mountpoint() error by statically linking in the 
+## mountpoint code.
+## DP: Author: Jaldhar H. Vyas <jaldhar@debian.org>
+
+. $(dirname $0)/DPATCH
+
+exit 0
+@DPATCH@
+diff -ruN dovecot-1.0.rc2.orig/src/plugins/quota/Makefile.am dovecot-1.0.rc2/src/plugins/quota/Makefile.am
+--- dovecot-1.0.rc2.orig/src/plugins/quota/Makefile.am	2006-06-17 14:02:55.000000000 -0400
++++ dovecot-1.0.rc2/src/plugins/quota/Makefile.am	2006-07-07 09:16:08.000000000 -0400
+@@ -12,6 +12,11 @@
+ module_LTLIBRARIES = \
+ 	lib10_quota_plugin.la
+ 
++# get some functions included which only plugins use. liblib should probably
++# be a shared library so this wouldn't be needed.
++unused_sources = \
++  ../../lib/mountpoint.c
++
+ lib10_quota_plugin_la_SOURCES = \
+ 	quota.c \
+         quota-count.c \
+@@ -20,7 +25,8 @@
+ 	quota-dirsize.c \
+ 	quota-maildir.c \
+         quota-plugin.c \
+-	quota-storage.c
++	quota-storage.c \
++	$(unused_sources)
+ 
+ noinst_HEADERS = \
+ 	quota.h \
--- dovecot-1.0.15.orig/debian/patches/sieve.dpatch
+++ dovecot-1.0.15/debian/patches/sieve.dpatch
@@ -0,0 +1,142 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## sieve.dpatch by Giuseppe Iuculano <giuseppe@iuculano.it>
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: No description.
+
+@DPATCH@
+diff -urNad dovecot-1.0.15~/dovecot-sieve/src/libsieve/bc_eval.c dovecot-1.0.15/dovecot-sieve/src/libsieve/bc_eval.c
+--- dovecot-1.0.15~/dovecot-sieve/src/libsieve/bc_eval.c	2009-09-23 10:03:24.000000000 +0200
++++ dovecot-1.0.15/dovecot-sieve/src/libsieve/bc_eval.c	2009-09-23 11:14:55.000000000 +0200
+@@ -475,7 +475,7 @@
+ 	int comparator=ntohl(bc[i+3].value);
+ 	int apart=ntohl(bc[i+4].value);
+ 	int count=0;
+-	char scount[3];
++	char scount[21];
+ 	int isReg = (match==B_REGEX);
+ 	int ctag = 0;
+ 	regex_t *reg;
+@@ -609,7 +609,7 @@
+      
+ 	if  (match == B_COUNT)
+ 	{
+-	    sprintf(scount, "%u", count);
++	    snprintf(scount, sizeof(scount), "%u", count);
+ 	    /* search through all the data */ 
+ 	    currd=datai+2;
+ 	    for (z=0; z<numdata && !res; z++)
+@@ -643,7 +643,7 @@
+ 	int relation=ntohl(bc[i+2].value);
+ 	int comparator=ntohl(bc[i+3].value);
+ 	int count=0;	
+-	char scount[3];
++	char scount[21];
+ 	int isReg = (match==B_REGEX);
+ 	int ctag = 0;
+ 	regex_t *reg;
+@@ -724,7 +724,7 @@
+ 	
+ 	if  (match == B_COUNT )
+ 	{
+-	    sprintf(scount, "%u", count);
++	    snprintf(scount, sizeof(scount), "%u", count);
+ 	    /*search through all the data*/ 
+ 	    currd=datai+2;
+ 	    for (z=0; z<numdata && !res; z++)
+diff -urNad dovecot-1.0.15~/dovecot-sieve/src/libsieve/script.c dovecot-1.0.15/dovecot-sieve/src/libsieve/script.c
+--- dovecot-1.0.15~/dovecot-sieve/src/libsieve/script.c	2009-09-23 10:03:24.000000000 +0200
++++ dovecot-1.0.15/dovecot-sieve/src/libsieve/script.c	2009-09-23 10:49:51.000000000 +0200
+@@ -526,9 +526,9 @@
+     if ((ret != SIEVE_OK) && interp->err) {
+ 	char buf[1024];
+ 	if (lastaction == -1) /* we never executed an action */
+-	    sprintf(buf, "%s", errmsg ? errmsg : sieve_errstr(ret));
++	    snprintf(buf, sizeof(buf), "%s", errmsg ? errmsg : sieve_errstr(ret));
+ 	else
+-	    sprintf(buf, "%s: %s", action_to_string(lastaction),
++	    snprintf(buf, sizeof(buf), "%s: %s", action_to_string(lastaction),
+ 		    errmsg ? errmsg : sieve_errstr(ret));
+  
+ 	ret |= interp->execute_err(buf, interp->interp_context,
+@@ -546,7 +546,7 @@
+ 	ret |= keep_ret;
+         if (keep_ret == SIEVE_OK)
+             snprintf(actions_string+strlen(actions_string),
+-		     sizeof(actions_string)-strlen(actions_string),
++		     ACTIONS_STRING_LEN-strlen(actions_string),
+ 		     "Kept\n");
+ 	else {
+ 	    implicit_keep = 0;	/* don't try an implicit keep again */
+@@ -599,7 +599,7 @@
+ 	    
+ 	    if (ret == SIEVE_OK)
+ 		snprintf(actions_string+strlen(actions_string),
+-			 sizeof(actions_string)-strlen(actions_string), 
++			 ACTIONS_STRING_LEN-strlen(actions_string), 
+ 			 "Rejected with: %s\n", a->u.rej.msg);
+ 
+ 	    break;
+@@ -615,7 +615,7 @@
+ 
+ 	    if (ret == SIEVE_OK)
+ 		snprintf(actions_string+strlen(actions_string),
+-			 sizeof(actions_string)-strlen(actions_string),
++			 ACTIONS_STRING_LEN-strlen(actions_string),
+ 			 "Filed into: %s\n",a->u.fil.mailbox);
+ 	    break;
+ 	case ACTION_KEEP:
+@@ -629,7 +629,7 @@
+ 			       &errmsg);
+ 	    if (ret == SIEVE_OK)
+ 		snprintf(actions_string+strlen(actions_string),
+-			 sizeof(actions_string)-strlen(actions_string),
++			 ACTIONS_STRING_LEN-strlen(actions_string),
+ 			 "Kept\n");
+ 	    break;
+ 	case ACTION_REDIRECT:
+@@ -643,7 +643,7 @@
+ 				   &errmsg);
+ 	    if (ret == SIEVE_OK)
+ 		snprintf(actions_string+strlen(actions_string),
+-			 sizeof(actions_string)-strlen(actions_string),
++			 ACTIONS_STRING_LEN-strlen(actions_string),
+ 			 "Redirected to %s\n", a->u.red.addr);
+ 	    break;
+ 	case ACTION_DISCARD:
+@@ -655,7 +655,7 @@
+ 				      &errmsg);
+ 	    if (ret == SIEVE_OK)
+ 		snprintf(actions_string+strlen(actions_string),
+-			 sizeof(actions_string)-strlen(actions_string),
++			 ACTIONS_STRING_LEN-strlen(actions_string),
+ 			 "Discarded\n");
+ 	    break;
+ 
+@@ -689,12 +689,12 @@
+ 
+ 		    if (ret == SIEVE_OK)
+ 			snprintf(actions_string+strlen(actions_string),
+-				 sizeof(actions_string)-strlen(actions_string),
++				 ACTIONS_STRING_LEN-strlen(actions_string),
+ 				 "Sent vacation reply\n");
+ 
+ 		} else if (ret == SIEVE_DONE) {
+ 		    snprintf(actions_string+strlen(actions_string),
+-			     sizeof(actions_string)-strlen(actions_string),
++			     ACTIONS_STRING_LEN-strlen(actions_string),
+ 			     "Vacation reply suppressed\n");
+ 
+ 		    ret = SIEVE_OK;
+diff -urNad dovecot-1.0.15~/dovecot-sieve/src/libsieve/sieve.y dovecot-1.0.15/dovecot-sieve/src/libsieve/sieve.y
+--- dovecot-1.0.15~/dovecot-sieve/src/libsieve/sieve.y	2009-09-23 10:03:24.000000000 +0200
++++ dovecot-1.0.15/dovecot-sieve/src/libsieve/sieve.y	2009-09-23 10:49:51.000000000 +0200
+@@ -922,7 +922,7 @@
+ 	else if (!strcmp(r, "ne")) {return NE;}
+ 	else if (!strcmp(r, "eq")) {return EQ;}
+ 	else{
+-	  sprintf(errbuf, "flag '%s': not a valid relational operation", r);
++	  snprintf(errbuf, sizeof(errbuf), "flag '%s': not a valid relational operation", r);
+ 	  yyerror(errbuf);
+ 	  return -1;
+ 	}
--- dovecot-1.0.15.orig/debian/patches/postgres_configure.dpatch
+++ dovecot-1.0.15/debian/patches/postgres_configure.dpatch
@@ -0,0 +1,54 @@
+#! /bin/sh -e
+
+## DP: Make configure use pg_config  if it is available.
+## DP: Author: Jaldhar H. Vyas <jaldhar@debian.org>
+
+. $(dirname $0)/DPATCH
+
+exit 0
+@DPATCH@
+--- dovecot-1.0.beta7/configure.in	2006-04-13 00:28:20.000000000 -0400
++++ dovecot-1.0.beta7/configure.in.debian	2006-04-13 00:42:04.000000000 -0400
+@@ -1392,21 +1392,29 @@
+ fi
+ 
+ if test $want_pgsql = yes; then
+-	# based on code from PHP
+-	for i in /usr /usr/local /usr/local/pgsql; do
+-		for j in include include/pgsql include/postgres include/postgresql ""; do
+-			if test -r "$i/$j/libpq-fe.h"; then
+-				PGSQL_INCLUDE=$i/$j
+-			fi
++	PGSQL_INCLUDE="`pg_config --includedir`"
++	if test "$PGSQL_INCLUDE" = ""; then
++		# based on code from PHP
++		for i in /usr /usr/local /usr/local/pgsql; do
++			for j in include include/pgsql include/postgres include/postgresql ""; do
++				if test -r "$i/$j/libpq-fe.h"; then
++					PGSQL_INCLUDE=$i/$j
++				fi
++			done
+ 		done
+-		for lib in lib lib64; do
+-		  for j in $lib $lib/pgsql $lib/postgres $lib/postgresql ""; do
+-			if test -f "$i/$j/libpq.so" || test -f "$i/$j/libpq.a"; then
+-				PGSQL_LIBDIR=$i/$j
+-			fi
+-		  done
++	fi
++	PGSQL_LIBDIR="`pg_config --libdir`"
++	if test "$PGSQL_LIBDIR" = ""; then
++		for i in /usr /usr/local /usr/local/pgsql; do
++			for lib in lib lib64; do
++			  for j in $lib $lib/pgsql $lib/postgres $lib/postgresql ""; do
++				if test -f "$i/$j/libpq.so" || test -f "$i/$j/libpq.a"; then
++					PGSQL_LIBDIR=$i/$j
++				fi
++		  	  done
++			done
+ 		done
+-	done
++	fi
+ 
+ 	old_LIBS=$LIBS
+ 	if test "$PGSQL_LIBDIR" != ""; then
--- dovecot-1.0.15.orig/debian/patches/00list
+++ dovecot-1.0.15/debian/patches/00list
@@ -0,0 +1,16 @@
+dovecot-MANAGESIEVE-9.3
+dovecot-1.0.15-managesieve-v9.3-security
+dovecot-example
+dovecot-sql
+dovecot-drac
+postgres_configure
+quota_mountpoint
+quota_v2
+exec_check_for_none
+protocols_none_by_default
+dovecot-ssl
+pam-error-information
+mbox_snarf
+autocreate
+CVE-2008-4577
+sieve.dpatch
--- dovecot-1.0.15.orig/debian/patches/protocols_none_by_default.dpatch
+++ dovecot-1.0.15/debian/patches/protocols_none_by_default.dpatch
@@ -0,0 +1,19 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## no_protocols_by_default.dpatch by  <soren@ubuntu.com>
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: Set protocols to "none" by default
+
+@DPATCH@
+diff -urNad dovecot-1.0.5~/dovecot-example.conf dovecot-1.0.5/dovecot-example.conf
+--- dovecot-1.0.5~/dovecot-example.conf	2007-10-08 13:38:23.111439900 +0200
++++ dovecot-1.0.5/dovecot-example.conf	2007-10-08 13:38:41.479672553 +0200
+@@ -21,7 +21,7 @@
+ # Protocols we want to be serving: imap imaps pop3 pop3s
+ # If you only want to use dovecot-auth, you can set this to "none".
+ #protocols = imap imaps
+-protocols =
++protocols = none
+ 
+ # IP or host address where to listen in for connections. It's not currently
+ # possible to specify multiple addresses. "*" listens in all IPv4 interfaces.
--- dovecot-1.0.15.orig/debian/patches/dovecot-MANAGESIEVE-9.3.dpatch
+++ dovecot-1.0.15/debian/patches/dovecot-MANAGESIEVE-9.3.dpatch
@@ -0,0 +1,17288 @@
+#! /bin/sh -e
+
+## ManageSieve patch v9.3 for dovecot-1.0.13.
+## 
+## Copyright (c) 2006-2008 by Stephan Bosch <stephan@rename-it.nl>
+## 
+## This patch is made for and partly based on the Dovecot Secure IMAP server
+## written by Timo Sirainen <tss@iki.fi>. The managesieve service is a modified
+## version of Dovecot's imap service implementation.
+## 
+## This patch includes the CMU Sieve sources copyrighted by the Carnegie Mellon
+## University. The authors are listed (after patching) at:
+##   src/lib-sieve/cmu/libsieve/AUTHORS
+## 
+## This patch is licenced under LGPLv2.1 (see COPYING.LGPL in the dovecot sources)
+## 
+## The included CMU Sieve code is licenced under a custom Carnegie Mellon
+## University licence as explained in the respective source files in:
+##   src/lib-sieve/cmu/libsieve/
+## 
+
+. $(dirname $0)/DPATCH
+
+exit 0
+@DPATCH@
+diff -r 894f003d9f5f README.managesieve
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/README.managesieve	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,372 @@
++Dovecot-MANAGESIEVE patch v9.3 for dovecot-1.0
++
++Compile
++-------
++
++After applying this patch, the usual ./configure, make, make install sequence 
++is not enough. First the automake/autoconf structure needs to be rebuilt to 
++include the ManageSieve sources in the compilation process. This requires 
++autotools to be installed on your system. 
++
++When you downloaded using Mercurial you have script called autogen.sh in your
++source tree. And you should proceed as specified on:
++
++http://wiki.dovecot.org/CompilingSource
++
++Otherwise, execute the following command inside the dovecot source tree:
++
++autoreconf -i
++
++After this you can continue the usual build process
++(http://wiki.dovecot.org/CompilingSource):
++
++./configure
++make
++sudo make install 
++ 
++Configure
++---------
++
++**NOTE**: If you have used the sieve plugin before and you have .dovecot.sieve 
++files in user directories, you are advised to make a backup first. Although the 
++managesieve daemon takes care to move these files to the sieve storage before it 
++is substituted with a symbolic link, this is not a very well tested operation, 
++meaning that there is a possibility that existing sieve scripts get lost.
++
++Along with all other binaries that dovecot uses, the managesieve and 
++managesieve-login binaries are installed during make install. The only thing you 
++need to do to activate the ManageSieve support in dovecot is to add managesieve 
++to the protocols= configuration line in your dovecot.conf. The managesieve 
++daemon will listen on port 2000 by default. As the implementation of the 
++managesieve daemon is largely based on the original IMAP implementation, it is 
++very similar in terms of configuration. In addition to most mail daemon config 
++settings, the managesieve daemon accepts a few more. The following settings can 
++be configured in the protocol managesieve section:
++
++listen = *:2000
++    IP or host address where to listen in for connections. 
++
++login_executable = /usr/libexec/dovecot/managesieve-login
++    Login executable location. 
++
++mail_executable = /usr/libexec/dovecot/managesieve
++    managesieve executable location. See mail_executable for IMAP for examples 
++    how this could be changed. 
++    
++managesieve_max_line_length = 65536
++    Maximum managesieve command line length in bytes. This setting is directly 
++    borrowed from IMAP. But, since long command lines are very unlikely with 
++    ManageSieve, changing this will not be very useful. 
++
++sieve_storage =
++    This specifies the path to the directory where the uploaded scripts are 
++    stored. In terms of '%' variable substitution it is identical to dovecot's 
++    mail_location setting used by the mail protocol daemons. Scripts are stored 
++    as separate files with extension .sieve, all other files are ignored when 
++    scripts are listed by a ManageSieve client. If this setting remains 
++    unspecified, the mail_location setting is used as explained below. 
++
++sieve = ~/.dovecot.sieve
++    Specifies the location of the symbolic link pointing to the active script in
++    the sieve storage directory. This must match the sieve setting used by 
++    deliver. Variable substitution with % is recognized. If a regular file 
++    exists at this location, it is moved to the sieve_storage location before 
++    the symbolic link is installed. It is renamed to dovecot.orig.sieve and 
++    therefore listed as dovecot.orig by a ManageSieve client. 
++
++mail_location =
++    If, for some inobvious reason, the sieve_storage remains unset, the 
++    managesieve daemon uses the specification of the mail_location to find out 
++    where to store the sieve files. However, this is provided only for backwards 
++    compatibility and you should always use the sieve_storage setting in stead. 
++
++managesieve_implementation_string = dovecot
++    To fool ManageSieve clients that are focused on CMU's timesieved you can 
++    specify the IMPLEMENTATION capability that the dovecot reports to clients 
++    (e.g. 'Cyrus timsieved v2.2.13'). 
++
++Scripts are stored at the location specified by the sieve_storage setting. The
++active sieve script is managed as a symbolic link pointing to the active script
++in the sieve storage direcotory. The location of this symlink can be specified
++with the 'sieve' setting. Make sure this setting is identical to what deliver is
++using for the Sieve plugin. The default location is ~/.dovecot.sieve. Note that
++if a file starting with '.' is placed inside a Maildir, it will be recognized as
++a folder, so try to avoid that.
++
++The current version of the managesieve daemon places the script storage 
++directory in the mail folder as specified by the 'mail_location' setting if no 
++'sieve_storage' is specified. Actually, it is placed in the CONTROL= directory 
++of 'mail_location' if specified, otherwise the sieve directory is placed in the 
++root of the mail location. In a mail or mail control directory, scripts are 
++always stored in a 'sieve' subdirectory. Note that for some mail storage types 
++(e.g. mbox) this script directory is listed as a mail folder, so be sure to put 
++the sieve scripts somewhere else if you can.
++
++A storage location specified by 'sieve_storage' is always generated 
++automatically if it does not exist (as far as the system permits the user to do 
++so; no root privileges are used). This is similar to the behaviour of the mail 
++daemons. Note that when 'mail_location' is used to specify the script storage 
++location, only the 'sieve' subdirectory is generated automatically.
++
++The following provides an example configuration for ManageSieve in dovecot.conf. 
++Only sections relevant to ManageSieve are shown. Refer to dovecot-example.conf 
++in your patched dovecot tree for a full example with comments, but don't forget 
++to add managesieve to the protocols setting if you use it.
++
++# Start imap, pop3 and managesieve services
++protocols = imap pop3 managesieve
++
++protocol managesieve {
++  # Specify an alternative address:port the daemon must listen on
++  # (default: *:2000)
++  #listen = localhost:2000
++
++  sieve=~/.dovecot.sieve
++  sieve_storage=~/sieve
++}
++
++Proxying
++--------
++
++Like Dovecot's imapd, the ManageSieve login daemon supports proxying to multiple
++backend servers. Although the underlying code is copied from the imapd sources
++for the most part, it has some ManageSieve-specifics that have not seen much
++testing. 
++
++The proxy configuration wiki page for POP3 and IMAP should apply to ManageSieve 
++as well:
++
++http://wiki.dovecot.org/PasswordDatabase/ExtraFields/Proxy
++
++Known Issues
++------------
++
++* Although this ManageSieve server should comply with the draft specification of 
++  the ManageSieve protocol, quite a few clients don't. This is particularly true 
++  for the TLS support. However, now that Cyrus' Timsieved has changed its 
++  behavior towards protocol compliance, all those clients will follow 
++  eventually. 
++
++  Clients known to have TLS issues:
++	- Thunderbird Sieve add-on: author is working on it
++	- AvelSieve: patch on the wiki:	http://wiki.dovecot.org/ManageSieve
++	- KMail + kio_sieve: ...?
++
++  Unfortunately, there is no reliable way to provide a workaround for this
++  problem. We will have to wait for the authors of these clients to make the
++  proper adjustments. 
++  
++* Other client issues:
++
++	- SmartSieve, WebSieve: 
++	    These clients are specifically written for Cyrus timsieved and fail on 
++	    multiple stages of the protocol when connected to Dovecot ManageSieve.
++	    
++* The current implementation of the daemon does not have quota enforcement as
++  recommended in the specification. So keep in mind that malicious users could
++  fill your filesystem with loads of spurious scriptfiles.
++  
++* The ANONYMOUS authentication mechanism is currently not supported and 
++  explicitly denied. 
++
++Contact Info
++------------
++
++Stephan Bosch <stephan at rename-it dot nl>
++IRC: Freenode, #dovecot, S[r]us
++
++Please use the Dovecot mailing list <dovecot at dovecot.org> for questions about 
++this package. You can post to the list without subscribing, the mail then waits 
++in a moderator queue for a while. See http://dovecot.org/mailinglists.html
++
++Design
++------
++
++The overall design of the daemon is entirely borrowed from the existing
++imap daemon. I have tried to apply the dovecot framework and programming
++paradigms as much as possible. This patch adds the following directories
++to the source directory:
++
++lib-managesieve: parser and quote functions (bound to disappear)
++lib-sievestorage: defines a storage for the sieve scripts (a bit crude)
++managesieve-login: the initial login daemon (derived from src/imap-login)
++managesieve: the actual managesieve daemon (derived form src/imap)
++lib-sieve: yet another copy of the cmu sieve library :/ (fixed for Dovecot 1.1)
++
++Currently the managesieve daemon simply plugs in the dovecot-master as any
++other mail protocol (imap,pop3). Since managesieve is not actually a mail
++protocol, this is probably not a good design practice. However, i designed
++this patch to be most uninvasive to the existing sources. I think Timo 
++knows best how to implement this link properly.
++
++The daemon currently implements all existing MANAGESIEVE commands except the 
++HAVESPACE command which always says 'ok'. It also implements the required
++support for UTF-8 strings. 
++
++Changelog
++---------
++
++* v9.3
++- Fixed bug that caused SASL mechanisms that require more than a single client
++  response to fail. Reported by Steffen Kaiser and occured when he tried using 
++  the (obsolete) LOGIN mechanism.
+++ Updated installation and configuration documentation to match the 
++  information provided in the wiki
++
++* v9.2
++- Fixed bug in tmp file generation for sieve-storage: errors other than EEXIST
++  would cause the daemon to sleep() loop indefinitely.
+++ Improved log lines to be more recognizable as being generated from
++  managesieve.
+++ Added short proxy configuration explanation to the README.managesieve file
+++ Added 'Known Issues' section to the README.managesieve file
+++ Fixed assert bug in sieve-storage occuring when save is canceled.
++
++* v9.1
+++ Updated patch to apply cleanly on dovecot-1.0.10
++
++* V9
+++ Definitively fixed the segfault mentioned in V8. It proved to be 
++  very time-constrained and thus hard to reproduce. The error turned out
++  to be related to the input handling of the login daemon during 
++  authentication. 
+++ Checked for changes in the imap daemon that weren't propagated to the 
++  managesieve implementation due to code duplication.
+++ Fixed a bug in the autodetection of the sieve storage location.
+++ Fixed bug in the sieve storage that failed to refresh the symlink if
++  the storage was moved. 
+++ Improved error handing in the sieve-storage implementation in various 
++  places. 
+++ Fixed the situation in which the active script link is located in the 
++  sieve storage. 
+++ Added managesieve configuration to dovecot-example.conf and made the example
++  in this file more concise. 
++
++* V8
+++ Fixed a few incompatibilities with 1.0.7 version. For instance, the "Logged
++  in" message is now sent by the -login process and not by the managesieve 
++  daemon anymore. This caused a segfault every once in a while. 
+++ Probably fixed the settings problem reported by Steffen Kaiser regarding 
++  login_dir. 'dovecot -n' now reports correct results, but testing will show
++  whether the whole problem is solved.
+++ The managesieve daemon now accepts the sieve_storage and sieve configuration
++  settings, so it is now possible to explicitly configure the location of the
++  sieve storage and the active script respectively. The daemon still falls back
++  to using the mail_location (MAIL) settings if nothing else is specified. 
+++ The cyrus timsieved does not use the + character in string literals and many
++  clients have adopted to this behaviour. The latest managesieve (08) advises to
++  accept a missing + from clients. The server should not send any + characters 
++  as well. This behavior is now implemented on the server. 
+++ Cleaned up sieve-storage.c: split up the sieve_storage_create function in 
++  various sub-functions for obtaining the various paths and directories.
+++ Forced manual intervention if rescueing a non-symlink file at the active script
++  path fails somehow. Previously, this presented the admin with a log message 
++  that it had just eaten the script, which is not very nice. 
+++ Restructured the README.managesieve file and added some more explanation with
++  regard to the configuration of the daemon.
++
++* V7 
++- Robin Breathe indicated that the regex capability was missing in the server's
++  SIEVE listing. It turns out I forgot to make arrangements for setting 
++  ENABLE_REGEX in the cmu libsieve sources, so the regex extension was not
++  compiled in. I copied the configure.in section regarding ENABLE_REGEX from 
++  dovecot-sieve-1.0.2 and that fixed the problem.
++
++* V6
++- Corked the client output stream while producing the capability greeting and on 
++  other some other occasions as well. Some naive client implementations expect to 
++  receive this as a single tcp frame and it is a good practice to do so anyway.
++  Using this change the Thunderbird sieve extension (v0.1.1) seemed to work. However,
++  scripts larger than a tcp frame still caused failures. All these issues are fixed
++  in the latest version of the sieve add-on (currently v0.1.4). 
++- Cleaned up the new proxy source. My editor made the indentation a complete mess
++  in terms of TABs vs spaces. 
++- Added TRYLATER response codes to BYE and NO messages where appropriate.  
++- Recopied the libsieve library into this patch to incorporate any changes that were
++  made (only sieve-cmu.c still needs to be compared to the old cmu-sieve.c). This 
++  also solves the __attribute__((unused)) GCC dependencies. These were fixed long
++  ago by Timo....  the code duplication beast strikes again. 
++- Removed spurious return value from void function in 
++  src/lib-sieve/sieve-implementation.c as reported by Robin Breathe. GCC fails to
++  report these issues. The function involved is currently not used and serves only
++  as an example on how dovecot could support multiple sieve backends... 
++
++* V5 
++- Applied patch by Uldis Pakuls to fix master_dump_settings bug
++- Added some compilation/installation info to this README
++- Moved README to source tree root as README.managesieve
++- Fixed minor error handling bug in sieve_storage.c with respect to a missing
++  root directory.
++- Now sieve capabilities are reported as they are specified by the implementing
++  library and not in forced upper case. The sieve RFC now explicitly states
++  that sieve capability identifiers are case-sensitive. This broke compatibility
++  with SquirrelMail/Avelsieve. 
++- Disabled ANONYMOUS login entirely until proper support is implemented. V4
++  claimed to do so as well, but in fact it only stopped announcing it.
++- Implemented managesieve-proxy. It is not so much a clean copy of imap-proxy,
++  since the managesieve greeting is much more complex and requires parsing. 
++  Configuration is identical to imap-proxy. This seems to be a little under-
++  documented however (http://wiki.dovecot.org/PasswordDatabase/ExtraFields).  
++
++* V4
++- Added managesieve_implementation_string setting to the managesieve 
++  configuration. This can be used to customize the default "IMPLEMENTATION" 
++  capability response.
++- Denied ANONYMOUS login until proper support is implemented
++- Fixed problem with authenticate command regarding continued responses. In
++  V3 only initial response would work. Problem was caused by rc2 -> rc28 
++  upgrade. One of the clear reasons why code duplication is a very bad idea.
++- Fixed readlink bug as indicated by Timo: return value of readlink can also
++  be -1.
++- Fixed bug in the regular file rescue code, as introduced in the previous 
++  version. Used stat instead of lstat. This caused the symlink to be rescued 
++  subsequently in the next activation, thus still overwriting the initially 
++  rescued script.
++
++* V3
++- Updated source to compile with dovecot 1.0.rc27 
++- Daemon now uses the same location for .dovecot.sieve as dovecot-lda
++  This is typically ~/.dovecot.sieve
++- If .dovecot.sieve is a regular file, it is now moved into the script storage as
++  dovecot.orig.sieve, preventing deletion of (important) active scripts 
++  upon upgrade.
++- Changed error handling to yield a BYE message when the managesieve 
++  daemon exits unexpectedly (upon login) before any commands are entered. 
++  Horde-ingo would wait indefinitely for a response. 
++
++* V2
++- Fixed the bug (missing CRLF) in the authenticate command
++- Modified the sieve storage library making the interface much less crude.
++- The scripts put on the server using the putscript command are now 
++  checked before they are accepted.
++- The reported SIEVE capability is now directly read from the sieve 
++  implementation (in this case cmu), listing much more than "FILEINTO 
++  VACATION".
++- Imported instance of libsieve source into this patch for implementation
++  of script checking and capability listing. THIS NEEDS TO BE CHANGED! 
++- Fixed some minor bugs in the putscript command
++
++TODO
++------------
++
++* Upgrade to dovecot-1.1 branch. 
++* Enforce protocol syntax better with some of the commands. Some 
++  commands still allow spurious extra arguments
++  --> Full protocol syntax conformance review. 
++* Implement proper support for anonymous login.
++* Implement listing of NOTIFY capability as specified in the latest versions
++  of the MANAGESIEVE draft.
++* Implement the HAVESPACE command properly. Currently it always says ok.
++  Maybe this should be linked to the mail quota system that is currently 
++  developed. 
++* Create proper process interface for generic non-mail protocols (Timo).
++* Resolve exessive code duplication
++* Uploaded scripts are syntax checked, but this patch includes another 
++  cmu-sieve source tree to implement this. This needs to be put in some
++  unified location in de dovecot(-sieve) tree. This will probably have to
++  wait until dovecot v2.0
++* Make the sieve storage a base class with (possibly) various 
++  implementations, just like mail-storage. Currently not very useful. 
++
++* Thorough testing...
++
+diff -r 894f003d9f5f configure.in
+--- a/configure.in	Sun Mar 09 12:50:11 2008 +0200
++++ b/configure.in	Sun May 04 16:00:59 2008 +0200
+@@ -9,6 +9,8 @@ AC_ISC_POSIX
+ AC_ISC_POSIX
+ AC_PROG_CC
+ AC_PROG_CPP
++AM_PROG_LEX
++AC_PROG_YACC
+ AC_HEADER_STDC
+ AC_C_INLINE
+ AC_PROG_LIBTOOL
+@@ -287,6 +289,16 @@ AC_ARG_WITH(pop3d,
+ 	fi,
+ 	want_pop3d=yes)
+ AM_CONDITIONAL(BUILD_POP3D, test "$want_pop3d" = "yes")
++
++AC_ARG_WITH(managesieve,
++[  --with-managesieve      Build MANAGESIEVE server (default)],
++  if test x$withval = xno; then
++    want_managesieve=no
++  else
++    want_managesieve=yes
++  fi,
++  want_managesieve=yes)
++AM_CONDITIONAL(BUILD_MANAGESIEVE, test "$want_managesieve" = "yes")
+ 
+ AC_ARG_WITH(deliver,
+ [  --with-deliver          Build mail delivery agent (default)],
+@@ -1812,6 +1824,11 @@ capability="IMAP4rev1 SASL-IR SORT THREA
+ capability="IMAP4rev1 SASL-IR SORT THREAD=REFERENCES MULTIAPPEND UNSELECT LITERAL+ IDLE CHILDREN NAMESPACE LOGIN-REFERRALS"
+ AC_DEFINE_UNQUOTED(CAPABILITY_STRING, "$capability", IMAP capabilities)
+ 
++dnl * Regexp library check, from Cyrus IMAP
++AC_SEARCH_LIBS(regcomp, rx regex, [
++  CFLAGS="$CFLAGS -DENABLE_REGEX"
++  AC_CHECK_HEADER(rxposix.h, CFLAGS="$CFLAGS -DHAVE_RX")])
++
+ CFLAGS="$CFLAGS $EXTRA_CFLAGS"
+ 
+ AC_CONFIG_HEADERS([config.h])
+@@ -1828,8 +1845,13 @@ src/lib-imap/Makefile
+ src/lib-imap/Makefile
+ src/lib-index/Makefile
+ src/lib-mail/Makefile
++src/lib-managesieve/Makefile
+ src/lib-ntlm/Makefile
+ src/lib-settings/Makefile
++src/lib-sievestorage/Makefile
++src/lib-sieve/Makefile
++src/lib-sieve/cmu/Makefile
++src/lib-sieve/cmu/libsieve/Makefile
+ src/lib-storage/Makefile
+ src/lib-storage/index/Makefile
+ src/lib-storage/index/maildir/Makefile
+@@ -1843,6 +1865,8 @@ src/imap/Makefile
+ src/imap/Makefile
+ src/imap-login/Makefile
+ src/login-common/Makefile
++src/managesieve/Makefile
++src/managesieve-login/Makefile
+ src/master/Makefile
+ src/pop3/Makefile
+ src/pop3-login/Makefile
+diff -r 894f003d9f5f dovecot-example.conf
+--- a/dovecot-example.conf	Sun Mar 09 12:50:11 2008 +0200
++++ b/dovecot-example.conf	Sun May 04 16:00:59 2008 +0200
+@@ -18,7 +18,7 @@
+ # Base directory where to store runtime data.
+ #base_dir = /var/run/dovecot/
+ 
+-# Protocols we want to be serving: imap imaps pop3 pop3s
++# Protocols we want to be serving: imap imaps pop3 pop3s managesieve
+ # If you only want to use dovecot-auth, you can set this to "none".
+ #protocols = imap imaps
+ 
+@@ -28,8 +28,8 @@
+ # interfaces depending on the operating system.
+ #
+ # If you want to specify ports for each service, you will need to configure
+-# these settings inside the protocol imap/pop3 { ... } section, so you can
+-# specify different ports for IMAP/POP3. For example:
++# these settings inside the protocol imap/pop3/managesieve { ... } section, 
++# so you can specify different ports for IMAP/POP3/MANAGESIEVE. For example:
+ #   protocol imap {
+ #     listen = *:10143
+ #     ssl_listen = *:10943
+@@ -37,6 +37,10 @@
+ #   }
+ #   protocol pop3 {
+ #     listen = *:10100
++#     ..
++#   }
++#   protocol managesieve {
++#     listen = *:12000
+ #     ..
+ #   }
+ #listen = *
+@@ -646,6 +650,48 @@ protocol pop3 {
+   #     missing. This option simply sends it if it's missing.
+   # The list is space-separated.
+   #pop3_client_workarounds = 
++}
++
++##
++## MANAGESIEVE specific settings
++##
++
++protocol managesieve {
++  # Login executable location.
++  #login_executable = /usr/libexec/dovecot/managesieve-login
++
++  # MANAGESIEVE executable location. See IMAP's mail_executable above for 
++  # examples how this could be changed.
++  #mail_executable = /usr/libexec/dovecot/managesieve
++
++  # Maximum MANAGESIEVE command line length in bytes. This setting is 
++  # directly borrowed from IMAP. But, since long command lines are very
++  # unlikely with MANAGESIEVE, changing this will not be very useful.  
++  #managesieve_max_line_length = 65536
++
++  # Specifies the location of the symlink pointing to the active script in
++  # the sieve storage directory. This must match the SIEVE setting used by
++  # deliver (refer to http://wiki.dovecot.org/LDA/Sieve#location for more
++  # info). Variable substitution with % is recognized.
++  sieve=~/.dovecot.sieve
++
++  # This specifies the path to the directory where the uploaded scripts must
++  # be stored. In terms of '%' variable substitution it is identical to
++  # dovecot's mail_location setting used by the mail protocol daemons.
++  sieve_storage=~/sieve
++
++  # If, for some inobvious reason, the sieve_storage remains unset, the 
++  # managesieve daemon uses the specification of the mail_location to find out 
++  # where to store the sieve files (see explaination in README.managesieve). 
++  # The example below, when uncommented, overrides any global mail_location 
++  # specification and stores all the scripts in '~/mail/sieve' if sieve_storage 
++  # is unset. However, you should always use the sieve_storage setting.
++  # mail_location = mbox:~/mail
++
++  # To fool managesieve clients that are focused on timesieved you can
++  # specify the IMPLEMENTATION capability that the dovecot reports to clients 
++  # (default: dovecot).
++  #managesieve_implementation_string = Cyrus timsieved v2.2.13
+ }
+ 
+ ##
+diff -r 894f003d9f5f src/Makefile.am
+--- a/src/Makefile.am	Sun Mar 09 12:50:11 2008 +0200
++++ b/src/Makefile.am	Sun May 04 16:00:59 2008 +0200
+@@ -4,6 +4,10 @@ endif
+ 
+ if BUILD_DELIVER
+ DELIVER = deliver
++endif
++
++if BUILD_MANAGESIEVE
++MANAGESIEVE = lib-managesieve lib-sievestorage lib-sieve managesieve managesieve-login 
+ endif
+ 
+ SUBDIRS = \
+@@ -26,5 +30,6 @@ SUBDIRS = \
+ 	imap \
+ 	$(POP3D) \
+ 	$(DELIVER) \
++	$(MANAGESIEVE) \
+ 	util \
+ 	plugins
+diff -r 894f003d9f5f src/lib-managesieve/Makefile.am
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-managesieve/Makefile.am	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,14 @@
++noinst_LIBRARIES = libmanagesieve.a
++
++AM_CPPFLAGS = \
++	-I$(top_srcdir)/src/lib \
++	-I$(top_srcdir)/src/lib-charset \
++	-I$(top_srcdir)/src/lib-mail
++
++libmanagesieve_a_SOURCES = \
++	managesieve-quote.c \
++	managesieve-parser.c 
++
++noinst_HEADERS = \
++	managesieve-quote.h \
++	managesieve-parser.h 
+diff -r 894f003d9f5f src/lib-managesieve/managesieve-parser.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-managesieve/managesieve-parser.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,672 @@
++#include "lib.h"
++#include "istream.h"
++#include "ostream.h"
++#include "strescape.h"
++#include "managesieve-parser.h"
++
++#define is_linebreak(c) \
++	((c) == '\r' || (c) == '\n')
++
++#define LIST_ALLOC_SIZE 7
++
++enum arg_parse_type {
++	ARG_PARSE_NONE = 0,
++	ARG_PARSE_ATOM,
++	ARG_PARSE_STRING,
++	ARG_PARSE_LITERAL,
++	ARG_PARSE_LITERAL_DATA
++};
++
++struct managesieve_parser {
++	/* permanent */
++	pool_t pool;
++	struct istream *input;
++	struct ostream *output;
++	size_t max_line_size;
++	enum managesieve_parser_flags flags;
++
++	/* reset by managesieve_parser_reset(): */
++	size_t line_size;
++	struct managesieve_arg_list *root_list;
++	struct managesieve_arg_list *cur_list;
++
++	enum arg_parse_type cur_type;
++	size_t cur_pos; /* parser position in input buffer */
++
++	int str_first_escape; /* ARG_PARSE_STRING: index to first '\' */
++	uoff_t literal_size; /* ARG_PARSE_LITERAL: string size */
++
++	const char *error;
++
++	unsigned int literal_skip_crlf:1;
++	unsigned int literal_nonsync:1;
++	unsigned int eol:1;
++	unsigned int fatal_error:1;
++};
++
++/* @UNSAFE */
++#define LIST_REALLOC(parser, old_list, new_size) \
++	p_realloc((parser)->pool, old_list, \
++		  sizeof(struct managesieve_arg_list) + \
++		  (old_list == NULL ? 0 : \
++		   sizeof(struct managesieve_arg_list) * (old_list)->alloc), \
++		  sizeof(struct managesieve_arg_list) * (new_size))
++
++static void managesieve_args_realloc(struct managesieve_parser *parser, size_t size)
++{
++	parser->cur_list = LIST_REALLOC(parser, parser->cur_list, size);
++	parser->cur_list->alloc = size;
++
++  parser->root_list = parser->cur_list;
++}
++
++struct managesieve_parser *
++managesieve_parser_create(struct istream *input, struct ostream *output,
++		   size_t max_line_size)
++{
++	struct managesieve_parser *parser;
++
++	parser = i_new(struct managesieve_parser, 1);
++        parser->pool = pool_alloconly_create("MANAGESIEVE parser", 8192);
++	parser->input = input;
++	parser->output = output;
++	parser->max_line_size = max_line_size;
++
++	managesieve_args_realloc(parser, LIST_ALLOC_SIZE);
++	return parser;
++}
++
++void managesieve_parser_destroy(struct managesieve_parser **parser)
++{
++	pool_unref((*parser)->pool);
++	i_free(*parser);
++	*parser = NULL;
++}
++
++void managesieve_parser_reset(struct managesieve_parser *parser)
++{
++	p_clear(parser->pool);
++
++	parser->line_size = 0;
++
++	parser->root_list = NULL;
++	parser->cur_list = NULL;
++
++	parser->cur_type = ARG_PARSE_NONE;
++	parser->cur_pos = 0;
++
++	parser->str_first_escape = 0;
++	parser->literal_size = 0;
++
++	parser->error = NULL;
++
++	parser->literal_skip_crlf = FALSE;
++	parser->eol = FALSE;
++
++	managesieve_args_realloc(parser, LIST_ALLOC_SIZE);
++}
++
++const char *managesieve_parser_get_error(struct managesieve_parser *parser, bool *fatal)
++{
++	*fatal = parser->fatal_error;
++	return parser->error;
++}
++
++/* skip over everything parsed so far, plus the following whitespace */
++static int managesieve_parser_skip_to_next(struct managesieve_parser *parser,
++				    const unsigned char **data,
++				    size_t *data_size)
++{
++	size_t i;
++
++	for (i = parser->cur_pos; i < *data_size; i++) {
++		if ((*data)[i] != ' ')
++			break;
++	}
++
++	parser->line_size += i;
++	i_stream_skip(parser->input, i);
++	parser->cur_pos = 0;
++
++	*data += i;
++	*data_size -= i;
++	return *data_size > 0;
++}
++
++static struct managesieve_arg *managesieve_arg_create(struct managesieve_parser *parser)
++{
++	struct managesieve_arg *arg;
++
++	i_assert(parser->cur_list != NULL);
++
++	/* @UNSAFE */
++	if (parser->cur_list->size == parser->cur_list->alloc)
++		managesieve_args_realloc(parser, parser->cur_list->alloc * 2);
++
++	arg = &parser->cur_list->args[parser->cur_list->size];
++	parser->cur_list->size++;
++
++	return arg;
++}
++
++static void managesieve_parser_save_arg(struct managesieve_parser *parser,
++				 const unsigned char *data, size_t size)
++{
++	struct managesieve_arg *arg;
++
++	arg = managesieve_arg_create(parser);
++
++	switch (parser->cur_type) {
++	case ARG_PARSE_ATOM:
++		/* simply save the string */
++		arg->type = MANAGESIEVE_ARG_ATOM;
++		arg->_data.str = p_strndup(parser->pool, data, size);
++		break;
++	case ARG_PARSE_STRING:
++		/* data is quoted and may contain escapes. */
++		i_assert(size > 0);
++
++		arg->type = MANAGESIEVE_ARG_STRING;
++		arg->_data.str = p_strndup(parser->pool, data+1, size-1);
++
++		/* remove the escapes */
++		if (parser->str_first_escape >= 0 &&
++		    (parser->flags & MANAGESIEVE_PARSE_FLAG_NO_UNESCAPE) == 0) {
++			/* -1 because we skipped the '"' prefix */
++			str_unescape(arg->_data.str +
++				     parser->str_first_escape-1);
++		}
++		break;
++	case ARG_PARSE_LITERAL_DATA:
++		if ((parser->flags & MANAGESIEVE_PARSE_FLAG_LITERAL_SIZE) != 0) {
++			/* save literal size */
++			arg->type = MANAGESIEVE_ARG_LITERAL_SIZE;
++			arg->_data.literal_size = parser->literal_size;
++		} else if ((parser->flags &
++			    MANAGESIEVE_PARSE_FLAG_LITERAL_TYPE) != 0) {
++			arg->type = MANAGESIEVE_ARG_LITERAL;
++			arg->_data.str = p_strndup(parser->pool, data, size);
++		} else {
++			arg->type = MANAGESIEVE_ARG_STRING;
++			arg->_data.str = p_strndup(parser->pool, data, size);
++		}
++		break;
++	default:
++		i_unreached();
++	}
++
++	parser->cur_type = ARG_PARSE_NONE;
++}
++
++static int is_valid_atom_char(struct managesieve_parser *parser, char chr)
++{
++	if (IS_ATOM_SPECIAL((unsigned char)chr)) {
++		parser->error = "Invalid characters in atom";
++		return FALSE;
++	} else if ((chr & 0x80) != 0) {
++		parser->error = "8bit data in atom";
++		return FALSE;
++	}
++
++	return TRUE;
++}
++
++static int managesieve_parser_read_atom(struct managesieve_parser *parser,
++				 const unsigned char *data, size_t data_size)
++{
++	size_t i;
++
++	/* read until we've found space, CR or LF. */
++	for (i = parser->cur_pos; i < data_size; i++) {
++		if (data[i] == ' ' || data[i] == ')' ||
++			 is_linebreak(data[i])) {
++			managesieve_parser_save_arg(parser, data, i);
++			break;
++		} else if (!is_valid_atom_char(parser, data[i]))
++			return FALSE;
++	}
++
++	parser->cur_pos = i;
++	return parser->cur_type == ARG_PARSE_NONE;
++}
++
++static int managesieve_parser_read_string(struct managesieve_parser *parser,
++				   const unsigned char *data, size_t data_size)
++{
++	size_t i;
++	int utf8_len;
++
++	/* QUOTED-CHAR        = SAFE-UTF8-CHAR / "\" QUOTED-SPECIALS
++	 * quoted             = <"> *QUOTED-CHAR <">
++	 *                    ;; limited to 1024 octets between the <">s
++	 */
++
++	/* read until we've found non-escaped ", CR or LF */
++	for (i = parser->cur_pos; i < data_size; i++) {
++		if (data[i] == '"') {
++			managesieve_parser_save_arg(parser, data, i);
++
++			i++; /* skip the trailing '"' too */
++			break;
++		}
++
++		if (data[i] == '\\') {
++			if (i+1 == data_size) {
++				/* known data ends with '\' - leave it to
++				   next time as well if it happens to be \" */
++				break;
++			}
++
++			/* save the first escaped char */
++			if (parser->str_first_escape < 0)
++				parser->str_first_escape = i;
++
++			/* skip the escaped char */
++			i++;
++
++			if ( !IS_QUOTED_SPECIAL(data[i]) ) {
++				parser->error = "Escaped quoted-string character is not a QUOTED-SPECIAL.";
++				return FALSE;
++			}
++
++			continue;
++		}
++
++		/* Enforce valid UTF-8
++		 */
++		if ( (utf8_len = UTF8_LEN(data[i])) == 0 ) {
++			parser->error = "String contains invalid character.";
++			return FALSE;
++		}
++		
++		if ( utf8_len > 1 ) {
++			bool overlong = FALSE;
++
++			if ( (i+utf8_len-1) >= data_size ) {
++				/* Known data ends in the middle of a UTF-8 character;
++				 * leave it to next time.
++				 */
++				break;
++			}
++
++			/* Check for overlong UTF-8 sequences */
++			switch (utf8_len) {
++			case 2:
++				if (!(data[i] & 0x1E)) overlong = TRUE;
++				break;
++			case 3:	
++				if (!(data[i] & 0x0F) && !(data[i+1] & 0x20)) overlong = TRUE;
++				break;
++			case 4:
++				if (!(data[i] & 0x07) && !(data[i+1] & 0x30)) overlong = TRUE;				
++				break;
++			case 5:
++				if (!(data[i] & 0x03) && !(data[i+1] & 0x38)) overlong = TRUE;
++				break;				
++			case 6:
++				if (!(data[i] & 0x01) && !(data[i+1] & 0x3C)) overlong = TRUE;
++				break;				
++			default:
++				i_unreached();
++			} 
++
++			if ( overlong ) {
++				parser->error = "String contains invalid/overlong UTF-8 character.";
++				return FALSE;
++			}
++
++			i++;
++			utf8_len--;
++	
++			/* Parse the series of UTF8_1 characters */
++			for (; utf8_len > 0; utf8_len--, i++ ) {  
++				if (!IS_UTF8_1(data[i])) {
++					parser->error = "String contains invalid UTF-8 character.";
++			    return FALSE;
++				}
++			}
++		}
++	}
++
++	parser->cur_pos = i;
++	return parser->cur_type == ARG_PARSE_NONE;
++}
++
++static int managesieve_parser_literal_end(struct managesieve_parser *parser)
++{
++	if ((parser->flags & MANAGESIEVE_PARSE_FLAG_LITERAL_SIZE) == 0) {
++		if (parser->line_size >= parser->max_line_size ||
++		    parser->literal_size >
++		    	parser->max_line_size - parser->line_size) {
++			/* too long string, abort. */
++			parser->error = "Literal size too large";
++			parser->fatal_error = TRUE;
++			return FALSE;
++		}
++	}
++
++	parser->cur_type = ARG_PARSE_LITERAL_DATA;
++	parser->literal_skip_crlf = TRUE;
++
++	parser->cur_pos = 0;
++	return TRUE;
++}
++
++static int managesieve_parser_read_literal(struct managesieve_parser *parser,
++				    const unsigned char *data,
++				    size_t data_size)
++{
++	size_t i, prev_size;
++
++	/* expecting digits + "}" */
++	for (i = parser->cur_pos; i < data_size; i++) {
++		if (data[i] == '}') {
++			parser->line_size += i+1;
++			i_stream_skip(parser->input, i+1);
++
++			return managesieve_parser_literal_end(parser);
++		}
++
++		if (parser->literal_nonsync) {
++			parser->error = "Expecting '}' after '+'";
++			return FALSE;
++		}
++
++		if (data[i] == '+') {
++			parser->literal_nonsync = TRUE;
++			continue;
++		}
++
++		if (data[i] < '0' || data[i] > '9') {
++			parser->error = "Invalid literal size";
++			return FALSE;
++		}
++
++		prev_size = parser->literal_size;
++		parser->literal_size = parser->literal_size*10 + (data[i]-'0');
++
++		if (parser->literal_size < prev_size) {
++			/* wrapped around, abort. */
++			parser->error = "Literal size too large";
++			return FALSE;
++		}
++	}
++
++	parser->cur_pos = i;
++	return FALSE;
++}
++
++static int managesieve_parser_read_literal_data(struct managesieve_parser *parser,
++					 const unsigned char *data,
++					 size_t data_size)
++{
++	if (parser->literal_skip_crlf) {
++
++		/* skip \r\n or \n, anything else gives an error */
++		if (data_size == 0)
++			return FALSE;
++
++		if (*data == '\r') {
++			parser->line_size++;
++			data++; data_size--;
++			i_stream_skip(parser->input, 1);
++
++			if (data_size == 0)
++				return FALSE;
++		}
++
++		if (*data != '\n') {
++			parser->error = "Missing LF after literal size";
++			return FALSE;
++		}
++
++		parser->line_size++;
++		data++; data_size--;
++		i_stream_skip(parser->input, 1);
++
++		parser->literal_skip_crlf = FALSE;
++
++		i_assert(parser->cur_pos == 0);
++	}
++
++	if ((parser->flags & MANAGESIEVE_PARSE_FLAG_LITERAL_SIZE) == 0) {
++		/* now we just wait until we've read enough data */
++		if (data_size < parser->literal_size) {
++			return FALSE;
++		} else {
++			managesieve_parser_save_arg(parser, data,
++					     (size_t)parser->literal_size);
++			parser->cur_pos = (size_t)parser->literal_size;
++			return TRUE;
++		}
++	} else {
++		/* we want to save only literal size, not the literal itself. */
++		parser->eol = TRUE;
++		managesieve_parser_save_arg(parser, NULL, 0);
++		return TRUE;
++	}
++}
++
++/* Returns TRUE if argument was fully processed. Also returns TRUE if
++   an argument inside a list was processed. */
++static int managesieve_parser_read_arg(struct managesieve_parser *parser)
++{
++	const unsigned char *data;
++	size_t data_size;
++
++	data = i_stream_get_data(parser->input, &data_size);
++	if (data_size == 0)
++		return FALSE;
++
++	while (parser->cur_type == ARG_PARSE_NONE) {
++		/* we haven't started parsing yet */
++		if (!managesieve_parser_skip_to_next(parser, &data, &data_size))
++			return FALSE;
++		i_assert(parser->cur_pos == 0);
++
++		switch (data[0]) {
++		case '\r':
++		case '\n':
++			/* unexpected end of line */
++			parser->eol = TRUE;
++			return FALSE;
++		case '"':
++			parser->cur_type = ARG_PARSE_STRING;
++			parser->str_first_escape = -1;
++			break;
++		case '{':
++			parser->cur_type = ARG_PARSE_LITERAL;
++			parser->literal_size = 0;
++			parser->literal_nonsync = FALSE;
++			break;
++		default:
++			if (!is_valid_atom_char(parser, data[0]))
++				return FALSE;
++			parser->cur_type = ARG_PARSE_ATOM;
++			break;
++		}
++
++		parser->cur_pos++;
++	}
++
++	i_assert(data_size > 0);
++
++	switch (parser->cur_type) {
++	case ARG_PARSE_ATOM:
++		if (!managesieve_parser_read_atom(parser, data, data_size))
++			return FALSE;
++		break;
++	case ARG_PARSE_STRING:
++		if (!managesieve_parser_read_string(parser, data, data_size))
++			return FALSE;
++		break;
++	case ARG_PARSE_LITERAL:
++		if (!managesieve_parser_read_literal(parser, data, data_size))
++			return FALSE;
++
++		/* pass through to parsing data. since input->skip was
++		   modified, we need to get the data start position again. */
++		data = i_stream_get_data(parser->input, &data_size);
++
++		/* fall through */
++	case ARG_PARSE_LITERAL_DATA:
++		if (!managesieve_parser_read_literal_data(parser, data, data_size))
++			return FALSE;
++		break;
++	default:
++		i_unreached();
++	}
++
++	i_assert(parser->cur_type == ARG_PARSE_NONE);
++	return TRUE;
++}
++
++/* ARG_PARSE_NONE checks that last argument isn't only partially parsed. */
++#define IS_UNFINISHED(parser) \
++        ((parser)->cur_type != ARG_PARSE_NONE || \
++	 (parser)->cur_list != parser->root_list)
++
++static int finish_line(struct managesieve_parser *parser, unsigned int count,
++		       struct managesieve_arg **args)
++{
++	parser->line_size += parser->cur_pos;
++	i_stream_skip(parser->input, parser->cur_pos);
++	parser->cur_pos = 0;
++
++	if (count >= parser->root_list->alloc) {
++		/* unused arguments must be NIL-filled. */
++		parser->root_list =
++			LIST_REALLOC(parser, parser->root_list, count+1);
++		parser->root_list->alloc = count+1;
++	}
++
++	parser->root_list->args[parser->root_list->size].type = MANAGESIEVE_ARG_EOL;
++
++	*args = parser->root_list->args;
++	return parser->root_list->size;
++}
++
++int managesieve_parser_read_args(struct managesieve_parser *parser, unsigned int count,
++			  enum managesieve_parser_flags flags, struct managesieve_arg **args)
++{
++	parser->flags = flags;
++
++	while (!parser->eol && (count == 0 || parser->root_list->size < count ||
++				IS_UNFINISHED(parser))) {
++		if (!managesieve_parser_read_arg(parser))
++			break;
++
++		if (parser->line_size > parser->max_line_size) {
++			parser->error = "MANAGESIEVE command line too large";
++			break;
++		}
++	}
++
++	if (parser->error != NULL) {
++		/* error, abort */
++		parser->line_size += parser->cur_pos;
++		i_stream_skip(parser->input, parser->cur_pos);
++		parser->cur_pos = 0;
++		*args = NULL;
++		return -1;
++	} else if ((!IS_UNFINISHED(parser) && count > 0 &&
++		    parser->root_list->size >= count) || parser->eol) {
++		/* all arguments read / end of line. */
++                return finish_line(parser, count, args);
++	} else {
++		/* need more data */
++		*args = NULL;
++		return -2;
++	}
++}
++
++int managesieve_parser_finish_line(struct managesieve_parser *parser, unsigned int count,
++			    enum managesieve_parser_flags flags,
++			    struct managesieve_arg **args)
++{
++	const unsigned char *data;
++	size_t data_size;
++	int ret;
++
++	ret = managesieve_parser_read_args(parser, count, flags, args);
++	if (ret == -2) {
++		/* we should have noticed end of everything except atom */
++		if (parser->cur_type == ARG_PARSE_ATOM) {
++			data = i_stream_get_data(parser->input, &data_size);
++			managesieve_parser_save_arg(parser, data, data_size);
++		}
++	}
++	return finish_line(parser, count, args);
++}
++
++const char *managesieve_parser_read_word(struct managesieve_parser *parser)
++{
++	const unsigned char *data;
++	size_t i, data_size;
++
++	data = i_stream_get_data(parser->input, &data_size);
++
++	for (i = 0; i < data_size; i++) {
++		if (data[i] == ' ' || data[i] == '\r' || data[i] == '\n')
++			break;
++	}
++
++	if (i < data_size) {
++		data_size = i + (data[i] == ' ' ? 1 : 0);
++		parser->line_size += data_size;
++		i_stream_skip(parser->input, data_size);
++		return p_strndup(parser->pool, data, i);
++	} else {
++		return NULL;
++	}
++}
++
++const char *managesieve_arg_string(struct managesieve_arg *arg)
++{
++	if (arg->type == MANAGESIEVE_ARG_STRING) 
++		return arg->_data.str;
++
++	return NULL;
++}
++
++int managesieve_arg_number
++	(struct managesieve_arg *arg, uoff_t *number)
++{
++	int i = 0;
++	const char *data;
++
++	*number = 0;
++
++	if (arg->type == MANAGESIEVE_ARG_ATOM) {
++		data = arg->_data.str;
++		while (data[i] != '\0') {
++			if (data[i] < '0' || data[i] > '9')
++				return -1;
++	
++			*number = (*number)*10 + (data[i] -'0');
++			i++;
++		}
++    
++		return 1;
++	}
++
++	return -1;
++}
++
++char *_managesieve_arg_str_error(const struct managesieve_arg *arg)
++{
++	i_panic("Tried to access managesieve_arg type %d as string", arg->type);
++	return NULL;
++}
++
++uoff_t _managesieve_arg_literal_size_error(const struct managesieve_arg *arg)
++{
++	i_panic("Tried to access managesieve_arg type %d as literal size", arg->type);
++	return 0;
++}
++
++struct managesieve_arg_list *_managesieve_arg_list_error(const struct managesieve_arg *arg)
++{
++	i_panic("Tried to access managesieve_arg type %d as list", arg->type);
++	return NULL;
++}
+diff -r 894f003d9f5f src/lib-managesieve/managesieve-parser.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-managesieve/managesieve-parser.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,189 @@
++#ifndef __MANAGESIEVE_PARSER_H
++#define __MANAGESIEVE_PARSER_H
++
++/*
++ * QUOTED-SPECIALS    = <"> / "\"
++ */
++#define IS_QUOTED_SPECIAL(c) \
++	((c) == '"' || (c) == '\\')
++
++/* 
++ * ATOM-SPECIALS      = "(" / ")" / "{" / SP / CTL / QUOTED-SPECIALS
++ */
++#define IS_ATOM_SPECIAL(c) \
++	((c) == '(' || (c) == ')' || (c) == '{' || \
++	 (c) <= 32 || (c) == 0x7f || \
++	 IS_QUOTED_SPECIAL(c)) 
++
++/* 
++ * CHAR               = %x01-7F
++ */
++#define IS_CHAR(c) \
++	(((c) & 0x80) == 0)
++
++/* 
++ * TEXT-CHAR          = %x01-09 / %x0B-0C / %x0E-7F
++ *                       ;; any CHAR except CR and LF
++ */
++#define IS_TEXT_CHAR(c) \
++	(IS_CHAR(c) && (c) != '\r' && (c) != '\n')
++
++/*
++ * SAFE-CHAR          = %x01-09 / %x0B-0C / %x0E-21 /
++ *                      %x23-5B / %x5D-7F
++ *                      ;; any TEXT-CHAR except QUOTED-SPECIALS
++ */
++#define IS_SAFE_CHAR(c) \
++	(IS_TEXT_CHAR(c) && !IS_QUOTED_SPECIAL(c))
++
++/* UTF8-1             = %x80-BF
++ */
++#define IS_UTF8_1(c) \
++	(((c) & 0xC0) == 0x80)
++
++/* UTF8-2             = %xC0-DF UTF8-1
++ */
++#define IS_UTF8_2S(c) \
++  (((c) & 0xE0) == 0xC0)
++
++/* UTF8-3             = %xE0-EF 2UTF8-1
++ */
++#define IS_UTF8_3S(c) \
++  (((c) & 0xF0) == 0xE0)
++
++/* UTF8-4             = %xF0-F7 3UTF8-1
++ */
++#define IS_UTF8_4S(c) \
++  (((c) & 0xF8) == 0xF0)
++
++/* UTF8-5             = %xF8-FB 4UTF8-1
++ */
++#define IS_UTF8_5S(c) \
++  (((c) & 0xFC) == 0xF8)
++
++/* UTF8-6             = %xFC-FD 5UTF8-1
++ */
++#define IS_UTF8_6S(c) \
++  (((c) & 0xFE) == 0xFC)
++
++/* SAFE-UTF8-CHAR     = SAFE-CHAR / UTF8-2 / UTF8-3 / UTF8-4 /
++ *                      UTF8-5 / UTF8-6
++ */
++#define UTF8_LEN(c) \
++  ( IS_SAFE_CHAR(c) ? 1 : \
++    IS_UTF8_2S(c) ? 2 : \
++    IS_UTF8_3S(c) ? 3 : \
++    IS_UTF8_4S(c) ? 4 : \
++    IS_UTF8_5S(c) ? 5 : \
++    IS_UTF8_6S(c) ? 6 : 0 )
++
++enum managesieve_parser_flags {
++	/* Set this flag if you wish to read only size of literal argument
++	   and not convert literal into string. Useful when you need to deal
++	   with large literal sizes. The literal must be the last read
++	   parameter. */
++	MANAGESIEVE_PARSE_FLAG_LITERAL_SIZE	= 0x01,
++	/* Don't remove '\' chars from string arguments */
++	MANAGESIEVE_PARSE_FLAG_NO_UNESCAPE	= 0x02,
++	/* Return literals as MANAGESIEVE_ARG_LITERAL instead of MANAGESIEVE_ARG_STRING */
++	MANAGESIEVE_PARSE_FLAG_LITERAL_TYPE	= 0x04
++};
++
++enum managesieve_arg_type {
++	MANAGESIEVE_ARG_ATOM = 0,
++	MANAGESIEVE_ARG_STRING,
++
++	/* literals are returned as MANAGESIEVE_ARG_STRING by default */
++	MANAGESIEVE_ARG_LITERAL,
++	MANAGESIEVE_ARG_LITERAL_SIZE,
++
++	MANAGESIEVE_ARG_EOL /* end of argument list */
++};
++
++struct managesieve_parser;
++
++struct managesieve_arg {
++	enum managesieve_arg_type type;
++
++	union {
++		char *str;
++		uoff_t literal_size;
++	} _data;
++};
++
++#define MANAGESIEVE_ARG_STR(arg) \
++	((arg)->type == MANAGESIEVE_ARG_STRING || \
++   (arg)->type == MANAGESIEVE_ARG_ATOM || \
++	 (arg)->type == MANAGESIEVE_ARG_LITERAL ? \
++	 (arg)->_data.str : _managesieve_arg_str_error(arg))
++
++#define MANAGESIEVE_ARG_LITERAL_SIZE(arg) \
++	(((arg)->type == MANAGESIEVE_ARG_LITERAL_SIZE) ? \
++	 (arg)->_data.literal_size : _managesieve_arg_literal_size_error(arg))
++
++struct managesieve_arg_list {
++	size_t size, alloc;
++	struct managesieve_arg args[1]; /* variable size */
++};
++
++
++/* Create new MANAGESIEVE argument parser. output is used for sending command
++   continuation requests for literals.
++
++   max_line_size can be used to approximately limit the maximum amount of
++   memory that gets allocated when parsing a line. Input buffer size limits
++   the maximum size of each parsed token.
++
++   Usually the largest lines are large only because they have a one huge
++   message set token, so you'll probably want to keep input buffer size the
++   same as max_line_size. That means the maximum memory usage is around
++   2 * max_line_size. */
++struct managesieve_parser *
++managesieve_parser_create(struct istream *input, struct ostream *output,
++		   size_t max_line_size);
++void managesieve_parser_destroy(struct managesieve_parser **parser);
++
++/* Reset the parser to initial state. */
++void managesieve_parser_reset(struct managesieve_parser *parser);
++
++/* Return the last error in parser. fatal is set to TRUE if there's no way to
++   continue parsing, currently only if too large non-sync literal size was
++   given. */
++const char *managesieve_parser_get_error(struct managesieve_parser *parser, bool *fatal);
++
++/* Read a number of arguments. This function doesn't call i_stream_read(), you
++   need to do that. Returns number of arguments read (may be less than count
++   in case of EOL), -2 if more data is needed or -1 if error occurred.
++
++   count-sized array of arguments are stored into args when return value is
++   0 or larger. If all arguments weren't read, they're set to NIL. count
++   can be set to 0 to read all arguments in the line. Last element in
++   args is always of type MANAGESIEVE_ARG_EOL. */
++int managesieve_parser_read_args(struct managesieve_parser *parser, unsigned int count,
++			  enum managesieve_parser_flags flags, struct managesieve_arg **args);
++
++/* just like managesieve_parser_read_args(), but assume \n at end of data in
++   input stream. */
++int managesieve_parser_finish_line(struct managesieve_parser *parser, unsigned int count,
++			    enum managesieve_parser_flags flags,
++			    struct managesieve_arg **args);
++
++/* Read one word - used for reading tag and command name.
++   Returns NULL if more data is needed. */
++const char *managesieve_parser_read_word(struct managesieve_parser *parser);
++
++/* Returns the managesieve argument as string. If it is no string this returns NULL */
++const char *managesieve_arg_string(struct managesieve_arg *arg);
++
++/* Returns 1 if the argument is a number. If it is no number this returns -1.
++ * The number itself is stored in *number.
++ */
++int managesieve_arg_number
++  (struct managesieve_arg *arg, uoff_t *number);
++
++/* Error functions */
++char *_managesieve_arg_str_error(const struct managesieve_arg *arg);
++uoff_t _managesieve_arg_literal_size_error(const struct managesieve_arg *arg);
++struct managesieve_arg_list *_managesieve_arg_list_error(const struct managesieve_arg *arg);
++
++#endif
+diff -r 894f003d9f5f src/lib-managesieve/managesieve-quote.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-managesieve/managesieve-quote.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,176 @@
++#include "lib.h"
++#include "str.h"
++#include "managesieve-parser.h"
++#include "managesieve-quote.h"
++
++/* Turn the value string into a valid MANAGESIEVE string or literal, no matter 
++ * what. QUOTED-SPECIALS are escaped, but any invalid (UTF-8) character
++ * is simply removed. Linebreak characters are not considered invalid, but
++ * they do force the generation of a string literal.
++ */
++void managesieve_quote_append(string_t *str, const unsigned char *value,
++		       size_t value_len, bool compress_lwsp)
++{
++	size_t i, extra = 0;
++	bool 
++		last_lwsp = TRUE, 
++		literal = FALSE, 
++		modify = FALSE,
++		escape = FALSE;
++	int utf8_len;
++
++ 	if (value == NULL) {
++		str_append(str, "\"\"");
++		return;
++	}
++
++	if (value_len == (size_t)-1)
++		value_len = strlen((const char *) value);
++
++	for (i = 0; i < value_len; i++) {
++		switch (value[i]) {
++		case ' ':
++		case '\t':
++			if (last_lwsp && compress_lwsp) {
++				modify = TRUE;
++				extra++;
++			}
++			last_lwsp = TRUE;
++			break;
++		case '"':
++		case '\\':
++			escape = TRUE;
++			last_lwsp = FALSE;
++			break;
++		case 13:
++		case 10:
++			literal = TRUE;
++			last_lwsp = TRUE;
++			break;
++		default:
++			/* Enforce valid UTF-8
++			 */
++			if ( (utf8_len=UTF8_LEN(value[i])) == 0 ) {
++				modify = TRUE;
++				extra++;
++				break;
++			}
++
++			if ( utf8_len > 1 ) {
++				int c = utf8_len - 1;
++
++		 		if ( (i+utf8_len-1) >= value_len ) {
++				  	/* Value ends in the middle of a UTF-8 character;
++					 * Kill the partial UTF-8 character
++					 */
++				  	extra += i + utf8_len - value_len;
++					modify = TRUE;
++					break;        	
++				}
++
++				/* Parse the series of UTF8_1 characters */
++				for (i++; c > 0; c--, i++ ) {
++					if (!IS_UTF8_1(value[i])) {
++						extra += utf8_len - c;
++						modify = TRUE;
++						break;
++					}
++				}
++			}
++   			
++			last_lwsp = FALSE;
++		}
++	}
++
++	if (!literal) {
++		/* no linebreak chars, return as (escaped) "string" */
++		str_append_c(str, '"');
++	} else {
++		/* return as literal */
++		str_printfa(str, "{%"PRIuSIZE_T"}\r\n", value_len - extra);
++	}
++
++	if (!modify && (literal || !escape))
++		str_append_n(str, value, value_len);
++	else {
++		last_lwsp = TRUE;
++		for (i = 0; i < value_len; i++) {
++			switch (value[i]) {
++			case '"':
++			case '\\':
++				last_lwsp = FALSE;
++				if (!literal) 
++					str_append_c(str, '\\');
++				str_append_c(str, value[i]);
++				break;
++			case ' ':
++			case '\t':
++				if (!last_lwsp || !compress_lwsp)
++					str_append_c(str, ' ');
++				last_lwsp = TRUE;
++				break;
++			case 13:
++			case 10:
++				last_lwsp = TRUE;
++				str_append_c(str, value[i]);
++				break;
++			default:
++	  			/* Enforce valid UTF-8
++				 */
++				if ( (utf8_len=UTF8_LEN(value[i])) == 0 ) 
++					break;
++      
++				if ( utf8_len > 1 ) {
++					int c = utf8_len - 1;
++					int j;
++
++					if ( (i+utf8_len-1) >= value_len ) {
++						/* Value ends in the middle of a UTF-8 character;
++						 * Kill the partial character
++						 */
++					 	i = value_len;
++						break;
++					}
++
++					/* Parse the series of UTF8_1 characters */
++					for (j = i+1; c > 0; c--, j++ ) {
++						if (!IS_UTF8_1(value[j])) {
++							/* Skip until after this erroneous character */
++							i = j;
++							break;
++						}
++					}
++
++					/* Append the UTF-8 character. Last octet is done later */
++					c = utf8_len - 1;
++					for (; c > 0; c--, i++ ) 
++						str_append_c(str, value[i]);
++				}
++     
++				last_lwsp = FALSE;
++				str_append_c(str, value[i]);
++				break;
++			}
++		}
++	}
++
++	if (!literal)
++		str_append_c(str, '"');
++}
++
++char *managesieve_quote(pool_t pool, const unsigned char *value, size_t value_len)
++{
++	string_t *str;
++	char *ret;
++
++	if (value == NULL)
++		return "\"\"";
++
++	t_push();
++	str = t_str_new(value_len + MAX_INT_STRLEN + 5);
++	managesieve_quote_append(str, value, value_len, TRUE);
++	ret = p_strndup(pool, str_data(str), str_len(str));
++	t_pop();
++
++	return ret;
++}
+diff -r 894f003d9f5f src/lib-managesieve/managesieve-quote.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-managesieve/managesieve-quote.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,17 @@
++#ifndef __IMAP_QUOTE_H
++#define __IMAP_QUOTE_H
++
++/* Return value suitable for sending to client, either as quoted-string or
++   literal. Note that this also converts TABs into spaces, multiple spaces
++   into single space and NULs to #128. */
++char *managesieve_quote(pool_t pool, const unsigned char *value, size_t value_len);
++
++/* Append to existing string. */
++void managesieve_quote_append(string_t *str, const unsigned char *value,
++		       size_t value_len, bool compress_lwsp);
++
++#define managesieve_quote_append_string(str, value, compress_lwsp) \
++	managesieve_quote_append(str, (const unsigned char *)(value), \
++			  (size_t)-1, compress_lwsp)
++
++#endif
+diff -r 894f003d9f5f src/lib-sieve/Makefile.am
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/Makefile.am	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,20 @@
++noinst_LTLIBRARIES = libsieve.la
++
++SUBDIRS = cmu
++
++INCLUDES = \
++	-I$(top_srcdir)/src/lib \
++	-I$(top_srcdir)/src/lib-storage \
++	-I$(top_srcdir)/src/lib-mail \
++	-I$(top_srcdir)/src/lib-sievestorage
++
++# FIXME: Make this configurable
++libsieve_la_LIBADD = \
++	cmu/libsieve_cmu.la
++
++libsieve_la_SOURCES = \
++	sieve-implementation.c 
++
++noinst_HEADERS = \
++	sieve-implementation.h \
++	sieve-implementation-private.h
+diff -r 894f003d9f5f src/lib-sieve/cmu/Makefile.am
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/Makefile.am	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,24 @@
++noinst_LTLIBRARIES = libsieve_cmu.la
++
++SUBDIRS = libsieve
++
++INCLUDES = \
++	-I$(top_srcdir)/src/lib \
++	-I$(top_srcdir)/src/lib-sieve \
++	-I$(top_srcdir)/src/lib-sievestorage \
++	-I$(top_srcdir)/src/lib-mail \
++	-I$(top_srcdir)/src/lib-storage \
++	-I$(top_srcdir)/src/deliver
++
++libsieve_cmu_la_LIBADD = libsieve/libsieve.la
++
++libsieve_cmu_la_SOURCES = \
++	cmu-sieve.c \
++	imparse.c \
++	map.c 
++	
++noinst_HEADERS = \
++    imparse.h \
++    libconfig.h \
++    map.h \
++    xmalloc.h
+diff -r 894f003d9f5f src/lib-sieve/cmu/cmu-sieve.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/cmu-sieve.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,498 @@
++
++/* Copyright (C) 2005-2006 Timo Sirainen */
++
++#include "lib.h"
++#include "ioloop.h"
++#include "array.h"
++#include "hostpid.h"
++#include "str.h"
++#include "str-sanitize.h"
++#include "write-full.h"
++#include "libsieve/sieve_interface.h"
++#include "sieve-script.h"
++#include "sieve-implementation-private.h"
++
++#include <fcntl.h>
++#include <unistd.h>
++#include <sys/stat.h>
++#include <sys/wait.h>
++
++extern struct sieve_implementation cmu_sieve;
++
++struct et_list *_et_list = NULL;
++
++/* data per script */
++typedef struct script_data {
++	string_t *errors;
++	int	num_errors;
++} script_data_t;
++
++typedef struct {
++	const char *id;
++	void *context;
++} sieve_msgdata_t;
++
++/* gets the header "head" from msg. */
++static int getheader(void *v, const char *phead, const char ***body)
++{
++	sieve_msgdata_t *m = v;
++
++	if (phead==NULL) return SIEVE_FAIL;
++	*body = (const char **)sieve_runenv_get_mail_headers(m->context, phead);
++
++	if (*body) {
++		return SIEVE_OK;
++	} else {
++		return SIEVE_FAIL;
++	}
++}
++
++static int getsize(void *mc, int *size)
++{
++	sieve_msgdata_t *md = mc;
++	uoff_t psize;
++
++	psize = sieve_runenv_get_mail_size(md->context);
++	if (psize == (uoff_t)-1)
++		return SIEVE_FAIL;
++
++	*size = psize;
++	return SIEVE_OK;
++}
++
++static int getenvelope(void *mc, const char *field, const char ***contents)
++{
++	array_t ARRAY_DEFINE(values, const char *);
++	sieve_msgdata_t *m = (sieve_msgdata_t *) mc;
++	const char *data;
++
++	// FIXME: Should prob. not put this on the default pool
++	ARRAY_CREATE(&values, default_pool, const char *, 1);
++
++	if (!sieve_runenv_get_envelope(m->context, field, &values) ) {
++		*contents = NULL;
++		return SIEVE_FAIL;
++	}
++
++	data = NULL;
++	array_append(&values, &data, 1);
++
++	*contents = array_count(&values) == 1 ? NULL :
++	  array_get_modifyable(&values, NULL);
++
++	return *contents != NULL ? SIEVE_OK : SIEVE_FAIL;
++}
++
++static int sieve_redirect(void *ac, 
++			  void *ic __attr_unused__, 
++			  void *sc __attr_unused__, void *mc, const char **errmsg)
++{
++	sieve_redirect_context_t *rc = (sieve_redirect_context_t *) ac;
++	sieve_msgdata_t *m = mc;
++	const char *dupeid;
++	int res;
++
++	/* if we have a msgid, we can track our redirects */
++	dupeid = m->id == NULL ? NULL : t_strdup_printf("%s-%s", m->id, rc->addr);
++	if (dupeid != NULL) {
++		/* ok, let's see if we've redirected this message before */
++		if (sieve_runenv_is_duplicate(m->context, dupeid, strlen(dupeid))) {
++			/*duplicate_log(m->id, sd->username, "redirect");*/
++			i_info("discarded duplicate forward (%s -> %s)",
++				str_sanitize(m->id, 80), str_sanitize(rc->addr, 80));
++			return SIEVE_OK;
++		}
++	}
++
++	if ((res = sieve_runenv_send_forward(m->context, rc->addr)) == 0) {
++		/* mark this message as redirected */
++		i_info("forwarded id %s to <%s>",
++			m->id == NULL ? "" : str_sanitize(m->id, 80),
++			str_sanitize(rc->addr, 80));
++		
++		if (dupeid != NULL) {
++			sieve_runenv_mark_duplicate(m->context,dupeid, strlen(dupeid), 
++				DUPLICATE_DEFAULT_KEEP);
++		}
++		return SIEVE_OK;
++	} 
++
++	*errmsg = "Error sending mail";
++	return SIEVE_FAIL;
++}
++
++static int sieve_discard(void *ac __attr_unused__, 
++			 void *ic __attr_unused__, 
++			 void *sc __attr_unused__, void *mc,
++			 const char **errmsg __attr_unused__)
++{
++	sieve_msgdata_t *md = mc;
++
++	/* ok, we won't file it, but log it */
++	i_info("discarded id %s", md->id == NULL ? "" : str_sanitize(md->id, 80));
++	return SIEVE_OK;
++}
++
++static int sieve_reject(void *ac, 
++			void *ic __attr_unused__, 
++			void *sc __attr_unused__, 
++			void *mc, const char **errmsg)
++{
++	sieve_reject_context_t *rc = (sieve_reject_context_t *) ac;
++	sieve_msgdata_t *md = (sieve_msgdata_t *) mc;
++	int res;
++
++	if ((res = sieve_runenv_send_rejection(md->context, rc->msg)) == 0) {
++		i_info("rejected id %s",
++			md->id == NULL ? "" : str_sanitize(md->id, 80));
++		return SIEVE_OK;
++	} else {
++		*errmsg = "Error sending mail";
++		return SIEVE_FAIL;
++  }
++  return SIEVE_FAIL;
++}
++
++static int sieve_fileinto(void *ac, 
++			  void *ic __attr_unused__,
++			  void *sc __attr_unused__, 
++			  void *mc,
++			  const char **errmsg __attr_unused__)
++{
++	sieve_fileinto_context_t *fc = (sieve_fileinto_context_t *) ac;
++	sieve_msgdata_t *md = (sieve_msgdata_t *) mc;
++
++	if (sieve_runenv_mail_save
++	    (md->context, fc->mailbox, fc->imapflags->flag, 
++	     fc->imapflags->nflags) < 0)
++		return SIEVE_FAIL;
++
++	i_info("saved mail to %s", fc->mailbox);
++	return SIEVE_OK;
++}
++
++static int sieve_keep(void *ac, 
++	void *ic __attr_unused__,
++	void *sc __attr_unused__, 
++	void *mc, const char **errmsg __attr_unused__)
++{
++	sieve_keep_context_t *kc = (sieve_keep_context_t *) ac;
++	sieve_msgdata_t *md = (sieve_msgdata_t *) mc;
++
++	if (sieve_runenv_mail_save
++	    (md->context, NULL, kc->imapflags->flag, 
++	     kc->imapflags->nflags) < 0)
++		return SIEVE_FAIL;
++
++	return SIEVE_OK;
++}
++
++static int sieve_notify(void *ac __attr_unused__,
++			void *interp_context __attr_unused__,
++			void *script_context __attr_unused__,
++			void *mc __attr_unused__,
++			const char **errmsg __attr_unused__)
++{
++	return SIEVE_FAIL;
++}
++
++static int autorespond(void *ac, 
++		       void *ic __attr_unused__,
++		       void *sc __attr_unused__,
++		       void *mc,
++		       const char **errmsg __attr_unused__)
++{
++	sieve_autorespond_context_t *arc = (sieve_autorespond_context_t *) ac;
++	sieve_msgdata_t *md = (sieve_msgdata_t *) mc;
++	int ret;
++
++	/* ok, let's see if we've responded before */
++	ret = sieve_runenv_is_duplicate(md->context, arc->hash, arc->len) ?
++	  SIEVE_DONE : SIEVE_OK;
++
++	if (ret == SIEVE_OK) {
++		sieve_runenv_mark_duplicate(md->context, arc->hash, arc->len, 
++					    arc->days * (24 * 60 * 60));
++	}
++
++	return ret;
++}
++
++static int send_response(void *ac, 
++	void *ic __attr_unused__, 
++	void *sc __attr_unused__, 
++	void *mc,
++	const char **errmsg)
++{
++	sieve_send_response_context_t *src = (sieve_send_response_context_t *) ac;
++	sieve_msgdata_t *md = mc;
++	const char *outmsgid;
++
++	if (sieve_runenv_send_message
++	    (md->context, src->fromaddr, src->addr, 
++	     src->subj, src->mime, src->msg, &outmsgid) == 0) {
++		sieve_runenv_mark_duplicate
++		  (md->context, outmsgid, strlen(outmsgid),	DUPLICATE_DEFAULT_KEEP);
++		return SIEVE_OK;
++	} else {
++		*errmsg = "Error sending mail";
++		return SIEVE_FAIL;
++	}
++}
++
++/* vacation support */
++sieve_vacation_t vacation = {
++    1,				/* min response */
++    31,				/* max response */
++    &autorespond,		/* autorespond() */
++    &send_response		/* send_response() */
++};
++
++/* imapflags support */
++static char *markflags[] = { "\\flagged" };
++static sieve_imapflags_t mark = { markflags, 1 };
++
++static int sieve_parse_error_handler(int lineno, const char *msg, 
++				     void *ic __attr_unused__,
++				     void *sc)
++{
++	script_data_t *sd = (script_data_t *) sc;
++
++	if (sd->errors == NULL) 
++		sd->errors = str_new(default_pool, 1024);
++
++	if (sd->num_errors == 1) 
++		str_append(sd->errors, "\r\n");
++    
++	str_printfa(sd->errors, "line %d: %s", lineno, msg);
++
++	if (sd->num_errors >= 1) 
++		str_append(sd->errors, "\r\n");
++
++	sd->num_errors++;
++	return SIEVE_OK;
++}
++
++static int sieve_execute_error_handler(const char *msg, 
++				       void *ic __attr_unused__,
++				       void *sc __attr_unused__,
++				       void *mc __attr_unused__)
++{
++ 	i_info("sieve runtime error: %s", msg);
++	return SIEVE_OK;
++}
++ 
++static sieve_interp_t *setup_sieve(void)
++{
++	sieve_interp_t *interp = NULL;
++	int res;
++
++	res = sieve_interp_alloc(&interp, NULL);
++	if (res != SIEVE_OK)
++		i_fatal("sieve_interp_alloc() returns %d\n", res);
++
++	res = sieve_register_redirect(interp, &sieve_redirect);
++	if (res != SIEVE_OK)
++		i_fatal("sieve_register_redirect() returns %d\n", res);
++	res = sieve_register_discard(interp, &sieve_discard);
++	if (res != SIEVE_OK)
++		i_fatal("sieve_register_discard() returns %d\n", res);
++	res = sieve_register_reject(interp, &sieve_reject);
++	if (res != SIEVE_OK)
++		i_fatal("sieve_register_reject() returns %d\n", res);
++	res = sieve_register_fileinto(interp, &sieve_fileinto);
++	if (res != SIEVE_OK)
++		i_fatal("sieve_register_fileinto() returns %d\n", res);
++	res = sieve_register_keep(interp, &sieve_keep);
++	if (res != SIEVE_OK)
++		i_fatal("sieve_register_keep() returns %d\n", res);
++	res = sieve_register_imapflags(interp, &mark);
++	if (res != SIEVE_OK)
++		i_fatal("sieve_register_imapflags() returns %d\n", res);
++	res = sieve_register_notify(interp, &sieve_notify);
++	if (res != SIEVE_OK)
++		i_fatal("sieve_register_notify() returns %d\n", res);
++	res = sieve_register_size(interp, &getsize);
++	if (res != SIEVE_OK)
++		i_fatal("sieve_register_size() returns %d\n", res);
++	res = sieve_register_header(interp, &getheader);
++	if (res != SIEVE_OK)
++		i_fatal("sieve_register_header() returns %d\n", res);
++	
++	res = sieve_register_envelope(interp, &getenvelope);
++	if (res != SIEVE_OK)
++		i_fatal("sieve_register_envelope() returns %d\n", res);
++	res = sieve_register_vacation(interp, &vacation);
++	if (res != SIEVE_OK)
++		i_fatal("sieve_register_vacation() returns %d\n", res);
++	res = sieve_register_parse_error(interp, &sieve_parse_error_handler);
++	if (res != SIEVE_OK)
++		i_fatal("sieve_register_parse_error() returns %d\n", res);
++	res = sieve_register_execute_error(interp,  &sieve_execute_error_handler);
++	if (res != SIEVE_OK)
++		i_fatal("sieve_register_execute_error() returns %d\n", res);
++
++	return interp;
++}
++
++static int
++_cmu_sieve_compile(sieve_interp_t *interp, script_data_t *sdata,
++		     const char *script_path, const char *compiled_path)
++{
++	struct stat st, st2;
++	sieve_script_t *script;
++	bytecode_info_t *bc;
++	const char *temp_path;
++	FILE *f;
++	int fd, ret;
++
++	if (stat(script_path, &st) < 0) {
++		if (errno == ENOENT)
++			return 0;
++		i_error("stat(%s) failed: %m", script_path);
++		return -1;
++	}
++	if (compiled_path != NULL) {
++		if (stat(compiled_path, &st2) < 0) {
++			if (errno != ENOENT) {
++				i_error("stat(%s) failed: %m", script_path);
++				return -1;
++			}
++		} else {
++			if (st.st_mtime < st2.st_mtime)
++				return 1;
++		}
++	}
++
++	/* need to compile */
++	f = fopen(script_path, "r");
++	if (f == NULL) {
++		i_error("fopen(%s) failed: %m", script_path);
++		return -1;
++	}
++
++	ret = sieve_script_parse(interp, f, sdata, &script);
++	if (ret != SIEVE_OK) {
++		if (sdata->errors == NULL)
++		  	sieve_set_error("parse error %d", ret);
++		else
++		  	sieve_set_error("%s", str_c(sdata->errors));
++		
++		return -1;
++	}
++
++	if (compiled_path != NULL) {
++		if (sieve_generate_bytecode(&bc, script) < 0) {
++			i_error("sieve_generate_bytecode() failed");
++			return -1;
++		}
++
++		/* write to temp file */
++		temp_path = t_strconcat(compiled_path, ".tmp", NULL);
++		fd = open(temp_path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
++		if(fd == -1) {
++			i_error("open(%s) failed: %m", temp_path);
++			return -1;
++		}
++
++		if (sieve_emit_bytecode(fd, bc) < 0) {
++			i_error("sieve_emit_bytecode() failed");
++			return -1;
++		}
++
++		if (close(fd) < 0)
++			i_error("close() failed: %m");
++
++		/* and finally replace the script */
++		if (rename(temp_path, compiled_path) < 0) {
++			i_error("rename(%s, %s) failed: %m", temp_path, compiled_path);
++			return -1;
++		}
++	}
++
++	return 1;
++}
++
++static int cmu_sieve_compile
++	(struct sieve_script *script, bool verify_only __attr_unused__)
++{
++	const char *script_path;
++	sieve_interp_t *interp;
++	script_data_t sdata;
++	int ret;
++	
++	script_path = sieve_script_filename(script);
++	
++	if (script_path == NULL)
++		return -1;
++	
++	interp = setup_sieve();
++	
++	memset(&sdata, 0, sizeof(sdata));
++	
++	ret = _cmu_sieve_compile(interp, &sdata, script_path, NULL);
++	
++	/* Let's not assume that an error string is created only when an error
++	 * occurs...better safe than sorry
++	 */
++	if (sdata.errors != NULL)
++		str_free(&sdata.errors);
++
++	return ret;
++}
++
++static int cmu_sieve_run
++	(struct sieve_script *script, void *context)
++{
++	const char *script_path;
++	sieve_interp_t *interp;
++	sieve_bytecode_t *bytecode;
++	script_data_t sdata;
++	sieve_msgdata_t mdata;
++	const char *compiled_path, *path;
++	int ret;
++
++	script_path = sieve_script_filename(script);
++
++	if (script_path == NULL)
++		return -1;
++
++	interp = setup_sieve();
++
++	memset(&sdata, 0, sizeof(sdata));
++
++	compiled_path = t_strconcat(script_path, "c", NULL);
++	ret = _cmu_sieve_compile(interp, &sdata, script_path, compiled_path);
++
++	if (sdata.errors != NULL) {
++		path = t_strconcat(script_path, ".err", NULL);
++		//dovecot_sieve_write_error_file(&sdata, path);
++		str_free(&sdata.errors);
++	}
++	if (ret <= 0)
++		return ret;
++
++	memset(&mdata, 0, sizeof(mdata));
++	mdata.context = context;
++	mdata.id = sieve_runenv_get_mail_first_header(context, "Message-ID");
++
++	if ((ret = sieve_script_load(compiled_path, &bytecode)) != SIEVE_OK) {
++		i_error("sieve_script_load(%s) failed: %d", compiled_path, ret);
++		return -1;
++	}
++
++	if (sieve_execute_bytecode(bytecode, interp,
++				   &sdata, &mdata) != SIEVE_OK)
++		return -1;
++
++	return 1;
++}
++
++struct sieve_implementation cmu_sieve = {
++	MEMBER(name) "cmu",
++	{	cmu_sieve_compile,
++		cmu_sieve_run,
++		sieve_listextensions
++	}	
++};
++
+diff -r 894f003d9f5f src/lib-sieve/cmu/imparse.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/imparse.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,57 @@
++/*
++ * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer. 
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in
++ *    the documentation and/or other materials provided with the
++ *    distribution.
++ *
++ * 3. The name "Carnegie Mellon University" must not be used to
++ *    endorse or promote products derived from this software without
++ *    prior written permission. For permission or any other legal
++ *    details, please contact  
++ *      Office of Technology Transfer
++ *      Carnegie Mellon University
++ *      5000 Forbes Avenue
++ *      Pittsburgh, PA  15213-3890
++ *      (412) 268-4387, fax: (412) 268-7395
++ *      tech-transfer@andrew.cmu.edu
++ *
++ * 4. Redistributions of any form whatsoever must retain the following
++ *    acknowledgment:
++ *    "This product includes software developed by Computing Services
++ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
++ *
++ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
++ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
++ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
++ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
++ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ *
++ *
++ */
++#include "imparse.h"
++
++int imparse_isatom(const char *s)
++{
++    int len = 0;
++
++    if (!*s) return 0;
++    for (; *s; s++) {
++	len++;
++	if (*s & 0x80 || *s < 0x1f || *s == 0x7f ||
++	    *s == ' ' || *s == '{' || *s == '(' || *s == ')' ||
++	    *s == '\"' || *s == '%' || *s == '*' || *s == '\\') return 0;
++    }
++    if (len >= 1024) return 0;
++    return 1;
++}
+diff -r 894f003d9f5f src/lib-sieve/cmu/imparse.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/imparse.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,6 @@
++#ifndef __IMPARSE_H
++#define __IMPARSE_H
++
++extern int imparse_isatom (const char *s);
++
++#endif
+diff -r 894f003d9f5f src/lib-sieve/cmu/libconfig.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libconfig.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,8 @@
++#ifndef __LIBCONFIG_H
++#define __LIBCONFIG_H
++
++#define IMAPOPT_RFC3028_STRICT 1
++
++#define config_getswitch(n) 1
++
++#endif
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/AUTHORS
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/AUTHORS	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,9 @@
++$Id$
++
++Larry Greenfield <leg+sieve@andrew.cmu.edu> wrote the first pass.
++
++Alexy Melnikov <alexey.melnikov@isode.com> submitted some bug fixes and 
++improvements.
++
++Ken Murchison <ken@oceana.com> took the ball, added more extensions
++than existed in the known world, and overall improved the code mightily.
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/Makefile.am
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/Makefile.am	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,76 @@
++pkglibexecdir = $(libexecdir)/dovecot
++
++noinst_LTLIBRARIES = libsieve.la
++
++AM_YFLAGS = -d -p $*
++
++AM_CPPFLAGS = \
++	-I$(top_srcdir) \
++	-I$(top_srcdir)/src/lib \
++	-I../
++
++addr-lex.c: addr-lex.l
++	$(LEX) -t -Paddr addr-lex.l > addr-lex.c
++
++sieve-lex.c: sieve-lex.l
++	$(LEX) -t sieve-lex.l > sieve-lex.c
++
++libsieve_la_SOURCES = \
++	addr.y \
++	sieve.y \
++	addr-lex.l \
++	sieve-lex.l \
++	bc_dump.c \
++	bc_emit.c \
++	bc_eval.c \
++	bc_generate.c \
++	comparator.c \
++	interp.c \
++	message.c \
++	parseaddr.c \
++	script.c \
++	sieve_err.c \
++	tree.c
++
++noinst_HEADERS = \
++	addr.h \
++	bytecode.h \
++	comparator.h \
++	interp.h \
++	message.h \
++	parseaddr.h \
++	script.h \
++	sieve.h \
++	sieve_err.h \
++	sieve_interface.h \
++	tree.h
++
++pkglibexec_PROGRAMS = sievec sieved
++
++sievec_SOURCES = \
++	sievec.c \
++	../map.c \
++	../imparse.c
++
++sieved_SOURCES = \
++	sieved.c \
++	../map.c
++
++sievec_LDADD = \
++	libsieve.la \
++	$(top_srcdir)/src/lib/liblib.a
++
++sieved_LDADD = \
++	libsieve.la \
++	$(top_srcdir)/src/lib/liblib.a
++
++notbuilt_sources =
++
++EXTRA_DIST = \
++	addr-lex.l \
++	sieve-lex.l \
++	AUTHORS \
++	COPYING \
++	NEWS \
++	README \
++	$(notbuilt_sources)
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/NEWS
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/NEWS	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,84 @@
++$Id$
++
++CMU Sieve 2.1
++-------------
++
++- Compliant with RFC 3028.  As a result, fileinto and redirect only
++  accept a single string and NOT a string-list.
++
++- Compliant with draft-martin-sieve-notify-01.  As a result, notify
++  actions will need to be updated to the new syntax.
++
++CMU Sieve 2.0
++-------------
++
++- Compliant with draft-showalter-sieve-11.txt and 
++  draft-showalter-sieve-vacation-03.txt.
++
++- Added support for the regex, imapflags, notify and subaddress extensions.
++  See README for references.
++
++- Verifies email addresses in redirect and vacation actions are syntactically
++  correct (compliant with RFC822).
++
++- Run-time error reporting.
++
++- Changed callback interface to use callback contexts instead of individual
++  parameters.  Also added an error string buffer for run-time error reporting.
++
++- Vacation will not reply to any message containing an "auto-submitted"
++  header containing anything other than "no".
++
++CMU Sieve 1.4
++-------------
++
++Now included with imapd distribution (hell, why not?).
++
++Error returning and recovering:
++	added error recovering to the parser (but not much!)
++	added error messages to the parser
++
++Working on error returning and error recovering.
++	run-time errors
++	detect some errors in lexer?
++
++Working on even better parsing:
++	verify addresses could be addresses
++	verify mailboxes could be mailboxes
++	verify outgoing headers can be headers
++
++CMU Sieve 1.3
++-------------
++
++Changed for integration with cyrus deliver.
++
++CMU Sieve 1.2
++-------------
++
++Added additional callbacks (ok, so I want to make my integration with deliver
++easier) and envelope and vacation support.
++
++Made it compile without libcyrus.
++It should compile without libcyrus, but then it does not implement the
++"address" test.	 That's just too much work to do when I have a neato
++library to do it for me.
++
++Todo:
++- regex matching
++
++CMU Sieve 1.1
++-------------
++
++- Updated to draft-showalter-sieve-07bis.txt
++
++- Simple API (see sieve_interface.h; currently mostly undocumented)
++
++- Implements all of the optional features except "envelope"
++
++- Maintains "if it parses, it probably runs" behavior. (Goal: minimize
++  run-time errors.)
++
++CMU Sieve 1.0
++-------------
++
++- prototype implementation
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/README
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/README	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,59 @@
++$Id$
++
++CMU Sieve 2.1
++-------------
++
++This code is typically distributed as part of Cyrus imapd 1.6 and higher.
++This code will be configured and compiled from the cyrus-imapd directory.
++
++Notes on implementation
++-----------------------
++
++This is an implementation of a simple Sieve API.  This API is
++well-suited for incorporating in other programs, but is not
++extensible.  (If there is interest, we may implement an extensible API
++in the future.)
++
++If you wish to compile Sieve without compiling all of imapd, you'll
++have to create a Makefile for it.  I recommend you use Makefile.in as
++a guide.
++
++It should compile without libcyrus, but then it does not implement the
++"address" test.	 That's just too much work to do when I have a neato
++library to do it for me.
++
++There's a simple "test" application included, which is not built by
++default (type "make test" to build it).  It expects:
++
++test <message> <script>
++
++And prints out the actions taken or errors encountered.  (This
++implementation will attempt all the actions or no actions.)
++
++Questions and comments to:
++Derrick Brashear (shadow+sieve@andrew.cmu.edu)
++
++References:
++
++[SIEVE] Showalter, T., "Sieve: A Mail Filtering Language",
++RFC 3028, January, 2001.
++
++[VACATION] Showalter, T., "Sieve: Vacation Extension",
++draft-showalter-sieve-vacation-04.txt, August, 2000.
++
++[IMAPFLAGS] Melnikov, A., "Sieve -- IMAP flag extension",
++draft-melnikov-sieve-imapflags-03.txt, July, 2000.
++
++[NOTIFY] Martin, T., Segmuller, W.,
++"Sieve -- An extension for providing instant notifications",
++draft-martin-sieve-notify-01.txt, June, 2001.
++
++[REGEX] Murchison, K., "Sieve: Regular Expression Extension",
++draft-murchison-sieve-regex-04.txt, August, 2001.
++
++[RELATIONAL] Segmuller, W., "Sieve Extension: Relational Tests",
++RFC 3431, December 2002.
++
++[SUBADDR] Murchison, K., "Sieve Email Filtering -- Subaddress Extension",
++RFC 3598, September 2003.
++
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/addr-lex.l
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/addr-lex.l	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,91 @@
++%{
++/*
++ * addr-lex.l -- RFC 822 address lexer
++ * Ken Murchison
++ * $Id$
++ */
++/***********************************************************
++        Copyright 1999 by Carnegie Mellon University
++
++                      All Rights Reserved
++
++Permission to use, copy, modify, and distribute this software and its
++documentation for any purpose and without fee is hereby granted,
++provided that the above copyright notice appear in all copies and that
++both that copyright notice and this permission notice appear in
++supporting documentation, and that the name of Carnegie Mellon
++University not be used in advertising or publicity pertaining to
++distribution of the software without specific, written prior
++permission.
++
++CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
++FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
++ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
++OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++******************************************************************/
++
++#include "addr.h"
++#include <string.h>
++
++#undef YY_INPUT
++#define YY_INPUT(b, r, ms) (r = addrinput(b, ms))
++
++int addrinput(char *buf, int max_size);
++void addrerror(const char *);
++
++static int ncom;	/* number of open comments */
++%}
++
++%option noyywrap
++%option nounput
++%option prefix="addr"
++
++%x QSTRING DOMAINLIT COMMENT
++
++%%
++
++\"				{ BEGIN QSTRING; return yytext[0]; }
++\[				{ BEGIN DOMAINLIT; return yytext[0]; }
++\(				{ ncom = 1; BEGIN COMMENT; }
++\)				{ addrerror("address parse error, "
++					  "unexpected `')'' "
++					  "(unbalanced comment)");
++				  yyterminate(); }
++
++[^\(\)<>@,;:\\".\[\] \n\r]+	return ATOM;
++
++[\t \n\r]+			/* ignore whitespace */
++.				return yytext[0];
++
++<QSTRING>([^\n\r"\\]|\\.)*	return QTEXT;
++<QSTRING>\"			{ BEGIN INITIAL; return yytext[0]; }
++
++<DOMAINLIT>([^\[\]\n\r\\]|\\.)*	return DTEXT;
++<DOMAINLIT>\]			{ BEGIN INITIAL; return yytext[0]; }
++
++<COMMENT>([^\(\)\n\0\\]|\\.)*	/* ignore comments */
++<COMMENT>\(			ncom++;
++<COMMENT>\)			{ ncom--; if (ncom == 0) BEGIN INITIAL; }
++<COMMENT><<EOF>>		{ addrerror("address parse error, "
++					  "expecting `')'' "
++					  "(unterminated comment)");
++				  yyterminate(); }
++
++%%
++
++/* take input from address string provided by sieve parser */
++int addrinput(char *buf, int max_size)
++{
++    extern char *addrptr;	/* current position in address string */
++    size_t n;			/* number of characters to read from string */
++
++    n = (int)strlen(addrptr) < max_size ? (int)strlen(addrptr) : max_size;
++    if (n > 0) {
++	memcpy(buf, addrptr, n);
++	addrptr += n;
++    }
++    return n;
++}
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/addr.y
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/addr.y	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,91 @@
++%{
++/*
++ * addr.y -- RFC 822 address parser
++ * Ken Murchison
++ * $Id$
++ */
++/***********************************************************
++        Copyright 1999 by Carnegie Mellon University
++
++                      All Rights Reserved
++
++Permission to use, copy, modify, and distribute this software and its
++documentation for any purpose and without fee is hereby granted,
++provided that the above copyright notice appear in all copies and that
++both that copyright notice and this permission notice appear in
++supporting documentation, and that the name of Carnegie Mellon
++University not be used in advertising or publicity pertaining to
++distribution of the software without specific, written prior
++permission.
++
++CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
++FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
++ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
++OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++******************************************************************/
++
++#include <stdlib.h>
++#include <string.h>
++
++#include "addr.h"
++#include "script.h"
++#include "xmalloc.h"
++    
++int addrerror(char *msg);
++extern int yylex(void);
++
++#define YYERROR_VERBOSE /* i want better error messages! */
++%}
++
++%token ATOM QTEXT DTEXT
++
++%%
++sieve_address: addrspec			/* simple address */
++	| phrase '<' addrspec '>'	/* name & addr-spec */
++	;
++
++addrspec: localpart '@' domain		/* global-address */
++	;
++
++localpart: word				/* uninterpreted, case-preserved */
++	| word '.' localpart
++	;
++
++domain: subdomain
++	| subdomain '.' domain
++	;
++
++subdomain: domainref
++	| domainlit
++	;
++
++domainref: ATOM				/* symbolic reference */
++	;
++
++domainlit: '[' DTEXT ']'
++	;
++
++phrase: word
++	| word phrase
++	;
++
++word: ATOM
++	| qstring
++	;
++
++qstring: '"' QTEXT '"'
++	;
++
++%%
++
++/* copy address error message into buffer provided by sieve parser */
++int addrerror(char *s)
++{
++    extern char addrerr[ADDRERR_SIZE];
++    
++    strlcpy(addrerr, s, sizeof(addrerr));
++    return 0;
++}
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/bc_dump.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/bc_dump.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,304 @@
++/* bc_generate.c -- sieve bytecode- almost flattened bytecode
++ * Rob Siemborski
++ * $Id$
++ */
++/***********************************************************
++        Copyright 2001 by Carnegie Mellon University
++
++                      All Rights Reserved
++
++Permission to use, copy, modify, and distribute this software and its
++documentation for any purpose and without fee is hereby granted,
++provided that the above copyright notice appear in all copies and that
++both that copyright notice and this permission notice appear in
++supporting documentation, and that the name of Carnegie Mellon
++University not be used in advertising or publicity pertaining to
++distribution of the software without specific, written prior
++permission.
++
++CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
++FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
++ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
++OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++******************************************************************/
++
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++ 
++#include "sieve_interface.h"
++#include "bytecode.h"
++
++ 
++struct bytecode_info 
++{
++    bytecode_t *data;/* pointer to almost-flat bytecode */
++    size_t scriptend; /* used by emit code to know final length of bytecode */
++    size_t reallen; /* allocated length of 'data' */
++};
++
++#if DUMPCODE
++
++/*this would work a lot better if we actually could tell how many levels deep in if statements we were.  currently it doesn't know*/
++
++static void print_spaces(int n)
++{
++    int temp_n=0;
++    while(temp_n++ < (n))
++	putchar(' ');
++}
++
++
++/* Dump a stringlist.  Return the last address used by the list */
++static int dump_sl(bytecode_info_t *d, int ip, int level) 
++{
++    int numstr = d->data[ip].listlen;
++    int i;
++    
++    for(i=0; i<numstr; i++) {
++	print_spaces(level*4);
++	printf(" {%d}",d->data[++ip].len);
++	printf("%s\n",d->data[++ip].str);
++    }
++    
++    return ip;
++}
++
++static int dump_test(bytecode_info_t *d, int ip, int level);
++
++/* Dump a testlist.  Return the last address used by the list */
++static int dump_tl(bytecode_info_t *d, int ip, int level) 
++{
++    int numtest = d->data[ip].listlen;
++    int i;
++    
++    for(i=0; i<numtest; i++) {
++	print_spaces(level*4);
++	printf(" (until %d)\n", d->data[++ip].jump);
++	ip = dump_test(d, ++ip, level);
++    }
++    
++    return ip;
++}
++
++/* Dump a test, return the last address used by the test */
++static int dump_test(bytecode_info_t *d, int ip, int level ) {
++
++    print_spaces(level*4);
++    switch(d->data[ip].op) {
++    case BC_TRUE:
++	printf("%d: TRUE\n",ip);
++	break;
++
++    case BC_FALSE:
++	printf("%d: FALSE\n",ip);
++	break;
++
++    case BC_NOT:
++	printf("%d: NOT TEST(\n",ip++);
++	/*   printf("  (until %d)\n", d->data[ip++].jump);*/
++	ip = dump_test(d,ip, level);
++	print_spaces(level*4);
++	printf("    )\n");
++	break;
++
++    case BC_SIZE:
++	printf("%d: SIZE TAG(%d) NUM(%d)\n",ip,
++	       d->data[ip+1].value, d->data[ip+2].value);
++	ip+=2;
++	break;
++
++    case BC_EXISTS:
++	printf("%d: EXISTS\n",ip++);
++	ip = dump_sl(d,ip,level);
++	break;
++
++    case BC_ALLOF:
++	printf("%d: ALLOF (\n",ip++);
++	ip = dump_tl(d,ip,level);
++	print_spaces(level*4);
++	printf(")\n");
++	break;
++
++    case BC_ANYOF:
++	printf("%d: ANYOF (\n",ip++);
++	ip = dump_tl(d,ip, level);
++	  print_spaces(level*4);
++	printf(")\n");
++	break;
++	    
++    case BC_HEADER:
++	printf("%d: HEADER (\n",ip++);
++	print_spaces(level*4);
++	if (d->data[ip].value == B_COUNT || d->data[ip].value == B_VALUE)
++	{
++	    printf("      MATCH:%d  RELATION:%d  COMP:%d HEADERS:\n", 
++		   d->data[ip].value, d->data[ip+1].value,d->data[ip+2].value);
++	} else {
++	    printf("      MATCH:%d COMP:%d HEADERS:\n",d->data[ip].value, d->data[ip+2].value);
++	}
++	ip+=3;
++	ip = dump_sl(d,ip,level);
++	ip++;
++	print_spaces(level*4);
++	printf("      DATA:\n");
++	ip = dump_sl(d,ip,level);
++	break;
++	
++    case BC_ADDRESS:
++    case BC_ENVELOPE:
++	printf("%d: %s (\n",ip++,
++	       d->data[ip].op == BC_ADDRESS ? "ADDRESS" : "ENVELOPE");
++	print_spaces(level*4);
++	if (d->data[ip].value == B_COUNT || d->data[ip].value == B_VALUE)
++	{
++	    printf("      MATCH:%d RELATION: %d COMP: %d TYPE: %d HEADERS:\n", 
++		   d->data[ip].value, d->data[ip+1].value, d->data[ip+2].value, d->data[ip+3].value);
++	} else {
++	    printf("      MATCH:%d COMP:%d TYPE:%d HEADERS:\n",
++		   d->data[ip].value,d->data[ip+1].value,d->data[ip+3].value);
++	}
++	ip+=4;
++	ip = dump_sl(d,ip,level); ip++;
++	print_spaces(level*4);
++	printf("      DATA:\n");
++	ip = dump_sl(d,ip,level);
++	break;
++
++    default:
++	printf("%d: TEST(%d)\n",ip,d->data[ip].op);
++	break;
++    }
++
++    return ip;
++}
++
++void dump(bytecode_info_t *d, int level) 
++{
++    int i;
++    printf("Dumping almost flattened bytecode\n\n");
++    
++    if(!d) return;
++    
++    for(i=0; i<d->scriptend; i++) {
++	print_spaces(level*4);
++	switch(d->data[i].op) {
++	case B_REJECT:
++	    printf("%d: REJECT {%d}%s\n",i,
++		   d->data[i+1].len,d->data[i+2].str);
++	    i+=2;
++	    break;
++	case B_IF:
++	    if (d->data[i+3].jump== -1)
++	    {
++		printf("%d: IF THEN(%d) POST(%d) TEST(\n",i,
++		       d->data[i+1].jump,d->data[i+2].jump);
++	    }
++	    else
++	    {
++		printf("%d: IF THEN(%d) ELSE(%d) POST(%d) TEST(\n",i,
++		       d->data[i+1].jump,d->data[i+2].jump,
++		       d->data[i+3].jump);
++	    }
++	    i = dump_test(d,i+4, level+1);
++	    printf(")\n");
++	    break;
++
++	case B_STOP:
++	    printf("%d: STOP\n",i);
++	    break;
++
++	case B_DISCARD:
++	    printf("%d: DISCARD\n",i);
++	    break;
++	    
++	case B_KEEP:
++	    printf("%d: KEEP\n",i);
++	    break;
++
++	case B_MARK:
++	    printf("%d: MARK\n",i);
++	    break;
++
++	case B_UNMARK:
++	    printf("%d: UNMARK\n",i);
++	    break;
++
++	case B_FILEINTO:
++	    printf("%d: FILEINTO {%d}%s\n",i,
++		   d->data[i+1].len,d->data[i+2].str);
++	    i+=2;
++	    break;
++
++	case B_REDIRECT:
++	    printf("%d: REDIRECT {%d}%s\n",i,
++		   d->data[i+1].len,d->data[i+2].str);
++	    i+=2;
++	    break;
++
++	case B_SETFLAG:
++	    printf("%d: SETFLAG\n",i);
++	    i=dump_sl(d,++i, level);
++	    break;
++
++	case B_ADDFLAG:
++	    printf("%d: ADDFLAG\n",i);
++	    i=dump_sl(d,++i,level);
++	    break;
++
++	case B_REMOVEFLAG:
++	    printf("%d: REMOVEFLAG\n",i);
++	    i=dump_sl(d,++i,level);
++	    break;
++
++	case B_DENOTIFY:
++	    printf("%d: DENOTIFY priority %d,comp %d %d  %s\n", 
++		   i,
++		   d->data[i+1].value,
++		   d->data[i+2].value,
++		   d->data[i+3].value,
++		   (d->data[i+4].len == -1 ? "[nil]" : d->data[i+5].str));
++	    i+=5;
++	    break;
++
++	case B_NOTIFY: 
++	    printf("%d: NOTIFY\n   METHOD(%s),\n   ID(%s),\n   OPTIONS",
++		   i,
++		   d->data[i+2].str,
++		   (d->data[i+3].len == -1 ? "[nil]" : d->data[i+4].str));
++	    i+=5;
++	    i=dump_sl(d,i,level);
++	    printf("   PRIORITY(%d),\n   MESSAGE({%d}%s)\n", 
++		   d->data[i+1].value, d->data[i+2].len,d->data[i+3].str);
++	    i+=3;
++	    break;
++
++	case B_VACATION:
++	    printf("%d:VACATION\n",i);
++	    i++;
++	    i=dump_sl(d,i,level);
++	    printf("SUBJ({%d}%s) MESG({%d}%s)\n DAYS(%d) MIME(%d)\n", 
++		   d->data[i+1].len, (d->data[i+1].len == -1 ? "[nil]" : d->data[i+2].str),
++		   d->data[i+3].len, (d->data[i+3].len == -1 ? "[nil]" : d->data[i+4].str),
++		   d->data[i+5].value, d->data[i+6].value);
++	    i+=6;
++	
++	    break;
++	case B_JUMP:
++	    printf("%d: JUMP HUH?  this shouldn't be here>?!",i);
++	    break;
++	case B_NULL:
++	    printf("%d: NULL\n",i);
++	    break;
++	default:
++	    printf("%d: %d\n",i,d->data[i].op);
++	    break;
++	}
++    }
++    printf("full len is: %d\n", d->scriptend);
++}
++#endif
++
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/bc_emit.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/bc_emit.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,672 @@
++/* bc_emit.c -- sieve bytecode - pass 2 of the compiler
++ * Rob Siemborski
++ * Jen Smith
++ * $Id$
++ */
++/***********************************************************
++        Copyright 2001 by Carnegie Mellon University
++
++                      All Rights Reserved
++
++Permission to use, copy, modify, and distribute this software and its
++documentation for any purpose and without fee is hereby granted,
++provided that the above copyright notice appear in all copies and that
++both that copyright notice and this permission notice appear in
++supporting documentation, and that the name of Carnegie Mellon
++University not be used in advertising or publicity pertaining to
++distribution of the software without specific, written prior
++permission.
++
++CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
++FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
++ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
++OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++******************************************************************/
++
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include "xmalloc.h"
++#include "sieve_interface.h"
++
++ 
++#include "bytecode.h"
++
++#include <sys/types.h>
++#include <unistd.h>
++
++
++#if DUMPCODE
++void dump(bytecode_info_t *d);
++#endif
++
++static inline int write_int (int fd, int x)
++{
++    int y=htonl(x);
++    return (write(fd, &y, sizeof(int)));
++}
++ 
++    
++
++struct bytecode_info 
++{
++    bytecode_t *data;/* pointer to almost-flat bytecode */
++    size_t scriptend; /* used by emit code to know final length of bytecode  */
++    size_t reallen; /* allocated length of 'data' */
++};
++
++/* Pad null bytes onto the end of the string we just wrote */
++/* returns -1 on failure or number of bytes written on success */
++static int align_string(int fd, int string_len) 
++{
++    /* Keep in mind that we always want to pad a string with *at least*
++     * one zero, that's why sometimes we have to pad with 4 */
++    int needed = sizeof(int) - (string_len % sizeof(int));
++    if (needed>= 0 && needed <=4)
++    {
++    	if(write(fd, "\0\0\0\0", needed) == -1) return -1;
++    }
++    return needed;
++}
++
++/*all functions keep codep up to date as they use it.
++  the amount that has been written to the file is maintained by the
++  filelen variable in bc_action_emit
++  the other bc_xxx_emit funtions keep track of how much they (and any functions they call) have written and return this value
++*/
++
++
++/* Write out a stringlist to a given file descriptor.
++ * return # of bytes written on success and -1 on error */
++
++/* stringlist: <# listitems>
++               <pos of listend (bytes)>
++               <string:(size)(aligned string)>
++*/
++static int bc_stringlist_emit(int fd, int *codep, bytecode_info_t *bc) 
++{
++    int len = bc->data[(*codep)++].len;
++    int i;
++    int ret;
++    int wrote = 2*sizeof(int);
++    int begin,end;
++
++    /* Write out number of items in the list */
++    if (write_int(fd, len)== -1) return -1 ;
++    
++    /* skip one spot end of list position*/
++    begin=lseek(fd,0,SEEK_CUR);
++    lseek(fd,sizeof(int),SEEK_CUR);
++    
++    /* Loop through all the items of the list, writing out length and string
++     * in sequence */
++    for(i=0; i < len; i++)
++    {
++	int datalen = bc->data[(*codep)++].len;
++	
++	if(write_int(fd, datalen) == -1) return -1;
++	wrote += sizeof(int);
++	
++	if(write(fd, bc->data[(*codep)++].str, datalen) == -1) return -1;
++	wrote += datalen;
++	
++	ret = align_string(fd,datalen);
++	if(ret == -1) return -1;
++	
++	wrote+=ret;
++    }
++    end=lseek(fd,0,SEEK_CUR);
++ 
++    /* go back and write end of list position */
++    lseek(fd,begin,SEEK_SET);
++    if(write_int(fd, end) == -1) return -1;
++
++    /* return to the end */
++    lseek(fd,end,SEEK_SET);
++    return wrote;
++}
++
++static int bc_test_emit(int fd, int *codep, bytecode_info_t *bc);
++
++/* Write out a testlist to a given file descriptor.
++ * return # of bytes written on success and -1 on error */
++static int bc_testlist_emit(int fd, int *codep, bytecode_info_t *bc) 
++{
++    int len = bc->data[(*codep)++].len;
++    int i;
++    int ret;
++    int begin, end;
++    int wrote = 2*sizeof(int);
++        
++    /* Write out number of items in the list */
++    if(write_int(fd, len)== -1) return -1;
++
++    /* skip one spot for end of list position*/
++    begin = lseek(fd, 0, SEEK_CUR);
++    lseek(fd, sizeof(int), SEEK_CUR);
++      
++    /* Loop through all the items of the list, writing out each
++     * test as we reach it in sequence. */
++    for(i=0; i < len; i++) {
++	int nextcodep = bc->data[(*codep)++].jump;
++	
++	ret = bc_test_emit(fd, codep, bc);
++	if(ret < 0 ) return -1;
++	
++	wrote+=ret;
++	*codep = nextcodep;
++    }
++    end = lseek(fd, 0, SEEK_CUR);
++
++    /* go back and write the end of list position */
++    lseek(fd,begin,SEEK_SET);
++    if(write_int(fd, end) == -1) return -1;
++
++    /*return to the end */
++    lseek(fd,end,SEEK_SET);
++
++    return wrote;
++}
++
++/* emit the bytecode for a test.  returns -1 on failure or size of
++ * emitted bytecode on success */
++static int bc_test_emit(int fd, int *codep, bytecode_info_t *bc) 
++{
++    int wrote=0;/* Relative offset to account for interleaved strings */
++    
++    
++    int ret; /* Temporary Return Value Variable */
++    
++    /* Output this opcode */
++    if(write_int(fd, bc->data[(*codep)].op) == -1)
++	return -1;
++    wrote += sizeof(int);
++    
++    switch(bc->data[(*codep)++].op) {
++    case BC_TRUE:
++    case BC_FALSE:
++	/* No parameter opcodes */
++	break;
++	
++    case BC_NOT:
++    {
++	/* Single parameter: another test */
++	ret = bc_test_emit(fd, codep, bc);
++	if(ret < 0)
++	    return -1;
++	else
++	    wrote+=ret;
++	break;
++    }
++    
++    case BC_ALLOF:
++    case BC_ANYOF:
++	/*where we jump to?*/
++	/* Just drop a testlist */
++	ret = bc_testlist_emit(fd, codep, bc);
++	if(ret < 0)
++	    return -1;
++	else
++	    wrote+=ret;
++	break;
++	
++    case BC_SIZE:
++	/* Drop tag and number */
++	if(write_int(fd, bc->data[(*codep)].value) == -1)
++	    return -1;
++	if(write_int(fd, bc->data[(*codep)+1].value) == -1)
++	    return -1;
++	
++	wrote += 2 * sizeof(int);
++	(*codep) += 2;
++	break;
++	
++    case BC_EXISTS:
++    {
++	int ret;
++	ret = bc_stringlist_emit(fd, codep, bc);
++	if(ret < 0) return -1;
++	wrote += ret;
++	break;
++    }
++    
++    case BC_HEADER:
++    {
++	int ret;
++	/* Drop match type */
++	if(write_int(fd, bc->data[(*codep)].value) == -1)
++	    return -1;
++	wrote += sizeof(int);
++	(*codep)++;
++	/*drop comparator */
++	if(write_int(fd, bc->data[(*codep)].value) == -1)
++	    return -1;
++	wrote += sizeof(int);
++	(*codep)++;    
++	/*now drop relation*/
++	if(write_int(fd, bc->data[(*codep)].value) == -1)
++	    return -1;
++	wrote += sizeof(int);
++	(*codep)++;
++	/* Now drop headers */
++	ret = bc_stringlist_emit(fd, codep, bc);
++	if(ret < 0) return -1;
++	wrote+=ret;
++	/* Now drop data */
++	ret = bc_stringlist_emit(fd, codep, bc);
++	if(ret < 0) return -1;
++	wrote+=ret;
++	break;
++    }
++    
++    case BC_ADDRESS:
++    case BC_ENVELOPE:
++    {
++	int ret;
++	/* Drop match type */
++	if(write_int(fd, bc->data[(*codep)].value) == -1)
++	    return -1;
++	wrote += sizeof(int);
++	(*codep)++;
++	/*drop comparator */
++	if(write_int(fd, bc->data[(*codep)].value) == -1)
++	    return -1;
++	wrote += sizeof(int);
++	(*codep)++;
++	/*now drop relation*/
++	if(write_int(fd, bc->data[(*codep)].value) == -1)
++	    return -1;
++	wrote += sizeof(int);
++	(*codep)++;
++	/*now drop address part*/
++	if(write_int(fd, bc->data[(*codep)].value) == -1)
++	    return -1;
++	wrote += sizeof(int);
++	(*codep)++;
++	/* Now drop headers */
++	ret = bc_stringlist_emit(fd, codep, bc);
++	if(ret < 0) return -1;
++	wrote+=ret;
++	/* Now drop data */
++	ret = bc_stringlist_emit(fd, codep, bc);
++	if(ret < 0) return -1;
++	wrote+=ret;
++	break;
++    }
++    
++    default:
++	/* Unknown testcode? */
++	return -1;
++    }
++    return wrote;
++}
++
++/* emit the bytecode to a file descriptor given a flattened parse tree
++ * returns -1 on failure, size of emitted bytecode on success.
++ *
++ * this takes care of everything except the comparisons */
++static int bc_action_emit(int fd, int codep, int stopcodep,
++			  bytecode_info_t *bc, int filelen) 
++{
++    int len; /* Temporary Length Variable */
++    int ret; /* Temporary Return Value Variable */
++    int start_filelen = filelen;
++    int i;
++    
++    /*debugging variable to check filelen*/
++    /*int location;*/
++    
++    /*syslog(LOG_DEBUG, "entered bc_action_emit with filelen: %d", filelen);*/
++    
++    /* All non-string data MUST be sizeof(int)
++       byte alligned so the end of each string may require a pad */
++    /*
++     * Note that for purposes of jumps you must multiply codep by sizeof(int)
++     */
++    while(codep < stopcodep) {
++	/* Output this opcode */
++	if(write_int(fd, bc->data[codep].op) == -1)
++	    return -1; 
++	
++	filelen+=sizeof(int);
++	
++	switch(bc->data[codep++].op) {
++
++	case B_IF:
++	{
++	    /* IF
++	     *  test
++	     *  jump (false condition)
++	     *  then
++	     * (if there is an else) jump(finish) 
++	     * (if there is an else) else
++	     */
++
++	    int testEndLoc=-1;
++	    int testdist, thendist, elsedist;
++	    int c;
++	    
++	    int jumpFalseLoc=-1;/*this is the location that is being reserved
++				  for the first jump command
++				  we jump to the false condition of the test*/
++	    
++	    int jumpEndLoc=-1; /* this is the location that is being reserved
++				  for the optional jump command
++				  it jumps over the else statement to the end*/
++	    int jumpto=-1;
++	    int jumpop= B_JUMP;
++
++	    /*leave space to store the location of end of the test*/
++	    ret = lseek(fd, sizeof(int), SEEK_CUR);
++	    if(ret == -1) return ret;
++	    
++	    testEndLoc=filelen;
++	    filelen+=sizeof(int);
++	    
++	    /* spew the test */
++
++	    c=codep+3;
++	    testdist = bc_test_emit(fd, &c, bc);
++	    if(testdist == -1)return -1;
++	    filelen +=testdist;
++	    
++            /*store the location for hte end of the test
++	     *this is important for short circuiting of allof/anyof*/
++	    jumpto=filelen/4;
++	    if(lseek(fd, testEndLoc, SEEK_SET) == -1)
++		return -1;
++	    if(write_int(fd,jumpto) == -1)
++		return -1;
++
++	    if(lseek(fd,filelen,SEEK_SET) == -1)
++		return -1;
++
++	    /* leave space for jump */
++	    if(write_int(fd, jumpop) == -1)
++		return -1;
++	    ret = lseek(fd, sizeof(int), SEEK_CUR);
++	    if(ret == -1)
++		return ret;
++	    jumpFalseLoc=filelen+sizeof(int);
++	    
++	    filelen +=2*sizeof(int); /*jumpop + jump*/
++	    
++	    /* spew the then code */ 
++	    thendist = bc_action_emit(fd, bc->data[codep].value,
++				      bc->data[codep+1].value, bc,
++				      filelen);
++	 
++	    filelen+=thendist;
++	  	    
++	    /* there is an else case */
++	    if(bc->data[codep+2].value != -1)
++	    {
++		/* leave space for jump */
++		if(write_int(fd, jumpop) == -1)
++		    return -1;
++		ret = lseek(fd, sizeof(int), SEEK_CUR);
++		if(ret == -1)
++		    return ret;
++
++		jumpEndLoc=filelen+sizeof(int);
++		filelen+=2*sizeof(int);/*jumpop + jump*/
++	    }
++	  
++	    /*put previous jump to the end of the then code,
++	     *or the end of the jump if there is an else case */
++	    jumpto=filelen/4;
++	    if(lseek(fd, jumpFalseLoc, SEEK_SET) == -1)
++		return -1;
++	    if(write_int(fd,jumpto) == -1)
++		return -1;
++	    if(lseek(fd,filelen,SEEK_SET) == -1)
++		return -1;
++	    
++	    /* there is an else case */
++	    if(bc->data[codep+2].value != -1) {
++		/* spew the else code */
++		elsedist = bc_action_emit(fd, bc->data[codep+1].value,
++					 bc->data[codep+2].value, bc,
++					 filelen);
++	
++		filelen+=elsedist;
++		
++		/*put jump to the end of the else code*/
++	        jumpto=filelen/4;
++		if(lseek(fd, jumpEndLoc, SEEK_SET) == -1)
++		    return -1;
++		if(write_int(fd,jumpto) == -1)
++		    return -1;
++		if(lseek(fd,filelen,SEEK_SET) == -1)
++		    return -1;
++		
++		codep = bc->data[codep+2].value;
++	    } else {
++		codep = bc->data[codep+1].value;
++	    }
++	    
++	    break;
++	}
++	
++	case B_REJECT:
++	case B_FILEINTO:
++	case B_REDIRECT:
++	    /*just a string*/
++	    len = bc->data[codep++].len;
++	    if(write_int(fd,len) == -1)
++		return -1;
++
++	    filelen+=sizeof(int);
++	    
++	    if(write(fd,bc->data[codep++].str,len) == -1)
++		return -1;
++	    
++	    ret = align_string(fd, len);
++	    if(ret == -1)
++		return -1;
++
++	    filelen += len + ret;
++	    
++	    break; 
++
++	case B_SETFLAG:
++	case B_ADDFLAG:
++	case B_REMOVEFLAG:
++	    /* Dump just a stringlist */
++	    ret = bc_stringlist_emit(fd, &codep, bc);
++	    if(ret < 0)
++		return -1;
++	    filelen += ret;
++	    break;
++	    
++	case B_NOTIFY:
++	    /* method string, id string, options string list,
++	       priotity, Message String */
++	    /*method and id*/
++	    for(i=0; i<2; i++) {
++		len = bc->data[codep++].len;
++		if(write_int(fd,len) == -1)
++		    return -1;
++		filelen += sizeof(int);
++		if(len == -1)
++		{
++                    /* this will probably only happen for the id */
++		    /* this is a nil string */
++		    /* skip the null pointer and make up for it 
++		     * by adjusting the offset */
++		    codep++;
++		}
++		else
++		{	
++		    if(write(fd,bc->data[codep++].str,len) == -1)
++			return -1;
++		    
++		    ret = align_string(fd, len);
++		    if(ret == -1)
++			return -1;
++		    
++		    filelen += len + ret;
++		}
++		
++	    }
++	    /*options */
++	    ret = bc_stringlist_emit(fd, &codep, bc);
++	    if(ret < 0)
++		return -1;
++	    filelen+=ret;
++	    
++	    /*priority*/
++	    if(write_int(fd, bc->data[codep].value) == -1)
++		return -1;
++	    codep++;
++	    filelen += sizeof(int);
++	    
++	    len = bc->data[codep++].len;
++	    if(write_int(fd,len) == -1)
++		return -1;
++	    filelen += sizeof(int);
++	    
++	    if(write(fd,bc->data[codep++].str,len) == -1)
++		return -1;
++	    
++	    ret = align_string(fd, len);
++	    if(ret == -1) return -1;
++	    
++ 	    filelen += len + ret;
++	    break;
++
++		
++	case B_DENOTIFY:
++	    /* priority num,comptype  num,relat num, comp string*/ 
++
++	    /* priority*/
++	    if(write_int(fd, bc->data[codep].value) == -1)
++		return -1;
++	    filelen += sizeof(int);
++	    codep++;
++	    /* comptype */
++	    if(write_int(fd, bc->data[codep].value) == -1)
++		return -1;
++	    filelen += sizeof(int);
++	    codep++;
++	    /* relational*/
++	    if(write_int(fd, bc->data[codep].value) == -1)
++		return -1;
++	    filelen += sizeof(int);
++	    codep++;
++	    /* comp string*/
++	    
++	    len = bc->data[codep++].len;
++	    if(write_int(fd,len) == -1)
++		return -1;
++	    filelen += sizeof(int);
++	    
++	    if(len == -1)
++	    {
++		/* this is a nil string */
++		/* skip the null pointer and make up for it 
++		 * by adjusting the offset */
++		codep++;
++	    }
++	    else
++	    {
++		if(write(fd,bc->data[codep++].str,len) == -1)
++		    return -1;
++		
++		ret = align_string(fd, len);
++		if(ret == -1) return -1;
++		
++		filelen += len + ret;
++	    }
++	    	    break;
++	case B_VACATION:
++	    /* Address list, Subject String, Message String,
++	       Days (word), Mime (word) */
++	   
++	        /*new code-this might be broken*/
++	    ret = bc_stringlist_emit(fd, &codep, bc);
++	    if(ret < 0) return -1;
++	    filelen += ret;
++	    /*end of new code*/
++
++	    for(i=0; i<2; i++) {/*writing strings*/
++
++		/*write length of string*/
++		len = bc->data[codep++].len;
++		if(write_int(fd,len) == -1)
++		    return -1;
++		filelen += sizeof(int);
++		    
++		if(len == -1)
++		{
++		    /* this is a nil string */
++		    /* skip the null pointer and make up for it 
++		     * by adjusting the offset */
++		    codep++;
++		}
++		else
++		{
++		    /*write string*/
++		    if(write(fd,bc->data[codep++].str,len) == -1)
++			return -1;
++		    
++		    ret = align_string(fd, len);
++		    if(ret == -1) return -1;
++		    
++		    filelen += len + ret;
++		}
++		
++	    }
++	    /* Days*/
++	    if(write_int(fd,bc->data[codep].value) == -1)
++		return -1;
++	    codep++;
++	    filelen += sizeof(int);
++            /*Mime */
++	    if(write_int(fd,bc->data[codep].value) == -1)
++		return -1;
++	    codep++;
++	    filelen += sizeof(int);
++	    
++	    break;
++	case B_NULL:
++	case B_STOP:
++	case B_DISCARD:
++	case B_KEEP:
++	case B_MARK:
++	case B_UNMARK:
++	    /* No Parameters! */
++	    break;
++
++	default:
++	    /* Unknown opcode? */
++	    return -1;
++	}
++    }
++    return filelen - start_filelen;
++}
++
++/* spew the bytecode to disk */
++int sieve_emit_bytecode(int fd, bytecode_info_t *bc)  
++{
++    /* First output version number (4 bytes) */
++    int data = BYTECODE_VERSION;
++
++    /*this is a string, so it is happy*/
++    if(write(fd, BYTECODE_MAGIC, BYTECODE_MAGIC_LEN) == -1)
++	return -1;
++
++    if(write_int(fd, data) == -1) return -1;
++
++#if DUMPCODE
++    dump(bc);
++#endif
++
++    /*the sizeof(int) is to account for the version # at the begining*/
++    return bc_action_emit(fd, 0, bc->scriptend, bc, sizeof(int) + BYTECODE_MAGIC_LEN);
++}
++
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/bc_eval.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/bc_eval.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,1146 @@
++/* bc_eval.c - evaluate the bytecode
++ * $Id$
++ */
++/***********************************************************
++        Copyright 2001 by Carnegie Mellon University
++
++                      All Rights Reserved
++
++Permission to use, copy, modify, and distribute this software and its
++documentation for any purpose and without fee is hereby granted,
++provided that the above copyright notice appear in all copies and that
++both that copyright notice and this permission notice appear in
++supporting documentation, and that the name of Carnegie Mellon
++University not be used in advertising or publicity pertaining to
++distribution of the software without specific, written prior
++permission.
++
++CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
++FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
++ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
++OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++******************************************************************/
++
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include "sieve_interface.h"
++#include "interp.h"
++#include "message.h"
++
++#include "bytecode.h"
++
++#include "xmalloc.h"
++
++#include <string.h>
++#include <ctype.h>
++
++/**************************************************************************/
++/**************************************************************************/
++/**************************************************************************/
++/**************************EXECUTING BYTECODE******************************/
++/**************************************************************************/
++/**************************************************************************/
++/**************************************************************************/
++/**************************************************************************/
++
++/* Given a bytecode_input_t at the beginning of a string (the len block),
++ * return the string, the length, and the bytecode index of the NEXT
++ * item */
++int unwrap_string(bytecode_input_t *bc, int pos, const char **str, int *len)
++{
++    int local_len = ntohl(bc[pos].value);
++
++    pos++;
++    
++    if(local_len == -1) {
++	/* -1 length indicates NULL */
++	*str = NULL;
++    } else {
++	/* This cast is ugly, but necessary */
++	*str = (const char *)&bc[pos].str;
++	
++	/* Compute the next index */
++	pos += ((ROUNDUP(local_len+1))/sizeof(bytecode_input_t));
++    }
++    
++    if(len) *len = local_len;
++    
++    return pos;
++}
++
++
++/* this is used by notify to pass the options list to do_notify
++ * do_notify needs null-terminated (char *)[],
++ *  we have a stringlist, the beginning of which is pointed at by pos */
++static const char ** bc_makeArray(bytecode_input_t *bc, int *pos)
++{
++    int i;
++    const char** array;
++    int len = ntohl(bc[*pos].value);
++
++    (*pos)+=2; /* Skip # Values and Total Byte Length */
++  
++    array=(const char **)xmalloc((len+1) * sizeof(char *));
++
++    for (i=0; i<len; i++) {
++	*pos = unwrap_string(bc, *pos, &(array[i]), NULL);
++    }
++
++    array[i] = NULL;
++  
++    return array;
++}
++
++/* Compile a regular expression for use during parsing */
++static regex_t * bc_compile_regex(const char *s, int ctag,
++				  char *errmsg, size_t errsiz)
++{
++    int ret;
++    regex_t *reg = (regex_t *) xmalloc(sizeof(regex_t));
++    
++    if ( (ret=regcomp(reg, s, ctag)) != 0)
++    {
++	(void) regerror(ret, reg, errmsg, errsiz);
++	free(reg);
++	return NULL;
++    }
++    return reg;
++}
++
++/* Determine if addr is a system address */
++static int sysaddr(const char *addr)
++{
++    if (!strncasecmp(addr, "MAILER-DAEMON", 13))
++	return 1;
++
++    if (!strncasecmp(addr, "LISTSERV", 8))
++	return 1;
++
++    if (!strncasecmp(addr, "majordomo", 9))
++	return 1;
++
++    if (strstr(addr, "-request"))
++	return 1;
++
++    if (!strncmp(addr, "owner-", 6))
++	return 1;
++
++    return 0;
++}
++
++/* look for myaddr and myaddrs in the body of a header - return the match */
++static char* look_for_me(char *myaddr, int numaddresses,
++			       bytecode_input_t *bc, int i, const char **body)
++{
++    char *found = NULL;
++    int l;
++    int curra,x ;
++
++    /* loop through each TO header */
++    for (l = 0; body[l] != NULL && !found; l++) {
++	void *data = NULL, *marker = NULL;
++	char *addr;
++	
++	parse_address(body[l], &data, &marker);
++
++	/* loop through each address in the header */
++	while (!found &&
++	       ((addr = get_address(ADDRESS_ALL,&data, &marker, 1))!= NULL)) {
++
++	    if (!strcasecmp(addr, myaddr)) {
++		found = xstrdup(myaddr);
++		break;
++	    }
++
++	    curra=i;
++
++	    for(x=0; x<numaddresses; x++)
++	    {
++		void *altdata = NULL, *altmarker = NULL;
++		char *altaddr;
++		const char *str;
++
++		curra = unwrap_string(bc, curra, &str, NULL);
++		
++		/* is this address one of my addresses? */
++      		parse_address(str, &altdata, &altmarker);
++
++		altaddr = get_address(ADDRESS_ALL, &altdata, &altmarker, 1);
++
++		if (!strcasecmp(addr,altaddr)) {
++		    found=xstrdup(str);
++		    break;
++		}
++
++		free_address(&altdata, &altmarker);
++	    }
++
++	}
++	free_address(&data, &marker);
++    }
++
++    return found;
++}
++ 
++/* Determine if we should respond to a vacation message */
++static int shouldRespond(void * m, sieve_interp_t *interp,
++			 int numaddresses, bytecode_input_t* bc,
++			 int i, char **from, char **to)
++{
++    const char **body;
++    char buf[128];
++    char *myaddr = NULL;
++    int l = SIEVE_OK;
++    void *data = NULL, *marker = NULL;
++    char *tmp;
++    int curra, x;
++    char *found=NULL;
++    char *reply_to=NULL;
++  
++    /* is there an Auto-Submitted keyword other than "no"? */
++    strcpy(buf, "auto-submitted");
++    if (interp->getheader(m, buf, &body) == SIEVE_OK) {
++	/* we don't deal with comments, etc. here */
++	/* skip leading white-space */
++	while (*body[0] && isspace((int) *body[0])) body[0]++;
++	if (strcasecmp(body[0], "no")) l = SIEVE_DONE;
++    }
++
++    /* is there a Precedence keyword of "junk | bulk | list"? */
++    strcpy(buf, "precedence");
++    if (interp->getheader(m, buf, &body) == SIEVE_OK) {
++	/* we don't deal with comments, etc. here */
++	/* skip leading white-space */
++	while (*body[0] && isspace((int) *body[0])) body[0]++;
++	if (!strcasecmp(body[0], "junk") ||
++	    !strcasecmp(body[0], "bulk") ||
++	    !strcasecmp(body[0], "list"))
++	    l = SIEVE_DONE;
++    }
++
++    /* Note: the domain-part of all addresses are canonicalized */
++    /* grab my address from the envelope */
++    if (l == SIEVE_OK) {
++	strcpy(buf, "to");
++	l = interp->getenvelope(m, buf, &body);
++	
++	if (body[0]) {  
++	    parse_address(body[0], &data, &marker);
++	    tmp = get_address(ADDRESS_ALL, &data, &marker, 1);
++	    myaddr = (tmp != NULL) ? xstrdup(tmp) : NULL;
++	    free_address(&data, &marker);
++	}  
++    }  
++  
++    if (l == SIEVE_OK) {
++	strcpy(buf, "from");
++	l = interp->getenvelope(m, buf, &body);
++    }
++    if (l == SIEVE_OK && body[0]) {
++	/* we have to parse this address & decide whether we
++	   want to respond to it */
++	parse_address(body[0], &data, &marker);
++	tmp = get_address(ADDRESS_ALL, &data, &marker, 1);
++	reply_to = (tmp != NULL) ? xstrdup(tmp) : NULL;
++	free_address(&data, &marker);
++
++	/* first, is there a reply-to address? */
++	if (reply_to == NULL) {
++	    l = SIEVE_DONE;
++	}
++    
++	/* first, is it from me? */
++	if (l == SIEVE_OK && !strcmp(myaddr, reply_to)) {
++	    l = SIEVE_DONE;
++	}
++   
++	/* ok, is it any of the other addresses i've
++	   specified? */
++	if (l == SIEVE_OK)
++	{
++	    curra=i;
++	    for(x=0; x<numaddresses; x++) {
++		const char *address;
++
++		curra = unwrap_string(bc, curra, &address, NULL);
++		
++		if (!strcmp(address, reply_to))
++		    l=SIEVE_DONE;
++	    }
++	}
++   
++	/* ok, is it a system address? */
++	if (l == SIEVE_OK && sysaddr(reply_to)) {
++	    l = SIEVE_DONE;
++	}
++    }
++    if (l == SIEVE_OK) {
++	/* ok, we're willing to respond to the sender.
++	   but is this message to me?  that is, is my address
++	   in the TO, CC or BCC fields? */
++	if (strcpy(buf, "to"), 
++	    interp->getheader(m, buf, &body) == SIEVE_OK)
++	    found = look_for_me(myaddr, numaddresses ,bc, i, body);
++	if (!found && (strcpy(buf, "cc"),
++		       (interp->getheader(m, buf, &body) == SIEVE_OK)))
++	    found = look_for_me(myaddr, numaddresses, bc, i, body);
++	if (!found && (strcpy(buf, "bcc"),
++		       (interp->getheader(m, buf, &body) == SIEVE_OK)))
++	    found = look_for_me(myaddr, numaddresses, bc, i, body);
++	if (!found)
++	    l = SIEVE_DONE;
++    }
++    /* ok, ok, if we got here maybe we should reply */
++    if (myaddr) free(myaddr);
++    *from=found;
++    *to=reply_to;
++    return l;
++}
++
++/* Evaluate a bytecode test */
++static int eval_bc_test(sieve_interp_t *interp, void* m,
++			bytecode_input_t * bc, int * ip)
++{
++    int res=0; 
++    int i=*ip;
++    int x,y,z;/* loop variable */
++    int list_len; /* for allof/anyof/exists */
++    int list_end; /* for allof/anyof/exists */
++    int address=0;/*to differentiate between address and envelope*/
++    comparator_t * comp=NULL;
++    void * comprock=NULL;
++    int op= ntohl(bc[i].op);
++    
++    switch(op)
++    {
++    case BC_FALSE:
++	res=0; i++; break;
++
++    case BC_TRUE:
++	res=1; i++; break;
++
++    case BC_NOT:/*2*/
++	i+=1;
++	res = eval_bc_test(interp,m, bc, &i);
++	if(res >= 0) res = !res; /* Only invert in non-error case */
++	break;
++
++    case BC_EXISTS:/*3*/
++    {
++	int headersi=i+1;
++	const char** val;
++	int currh;
++
++	res=1;
++
++	list_len=ntohl(bc[headersi].len);
++	list_end=ntohl(bc[headersi+1].value)/4;
++
++	currh=headersi+2;
++
++	for(x=0; x<list_len && res; x++)
++	{
++	    const char *str;
++
++	    currh = unwrap_string(bc, currh, &str, NULL);
++	    
++	    if(interp->getheader(m,str, &val) != SIEVE_OK)
++		res = 0;
++	}
++
++	i=list_end; /* adjust for short-circuit */
++	break;
++    }
++    case BC_SIZE:/*4*/
++    {
++	int s;
++	int sizevar=ntohl(bc[i+1].value);
++	int x=ntohl(bc[i+2].value);
++	
++	if (interp->getsize(m, &s) != SIEVE_OK)
++	    break;
++	
++	if (sizevar ==B_OVER) {
++	    /* over */
++	    res= s > x;
++	} else {
++            /* under */
++	    res= s < x;
++	}
++	i+=3;
++	break;
++    }
++    case BC_ANYOF:/*5*/
++	res = 0;
++	list_len=ntohl(bc[i+1].len);
++	list_end=ntohl(bc[i+2].len)/4;
++	i+=3;
++
++	/* need to process all of them, to ensure our instruction pointer stays
++	 * in the right place */
++	for (x=0; x<list_len && !res; x++) { 
++	    int tmp;
++	    tmp = eval_bc_test(interp,m,bc,&i);
++	    if(tmp < 0) {
++		res = tmp;
++		break;
++	    }
++	    res = res || tmp;
++	}
++
++	i = list_end; /* handle short-circuting */
++
++	break; 
++    case BC_ALLOF:/*6*/ 
++        res = 1;     
++	list_len=ntohl(bc[i+1].len);
++	list_end=ntohl(bc[i+2].len)/4;
++	i+=3;
++
++	/* return 1 unless you find one that isn't true, then return 0 */
++	for (x=0; x<list_len && res; x++) {
++	    int tmp;
++	    tmp = eval_bc_test(interp,m,bc,&i);
++	    if(tmp < 0) {
++		res = tmp;
++		break;
++	    }
++	    res = res && tmp; 
++	}
++
++	i = list_end; /* handle short-circuiting */
++	
++	break;
++    case BC_ADDRESS:/*7*/
++	address=1;
++	/* fall through */
++    case BC_ENVELOPE:/*8*/
++    {
++	const char ** val;
++	void * data=NULL;
++	void * marker=NULL;
++	char * addr;
++	int addrpart=ADDRESS_ALL;/* XXX correct default behavior?*/
++
++ 	int headersi=i+5;/* the i value for the begining of the headers */
++	int datai=(ntohl(bc[headersi+1].value)/4);
++
++	int numheaders=ntohl(bc[headersi].len);
++	int numdata=ntohl(bc[datai].len);
++
++	int currh, currd; /* current header, current data */
++
++	int match=ntohl(bc[i+1].value);
++	int relation=ntohl(bc[i+2].value);
++	int comparator=ntohl(bc[i+3].value);
++	int apart=ntohl(bc[i+4].value);
++	int count=0;
++	char scount[3];
++	int isReg = (match==B_REGEX);
++	int ctag = 0;
++	regex_t *reg;
++	char errbuf[100]; /* Basically unused, as regexps are tested at compile */
++
++	/* set up variables needed for compiling regex */
++	if (isReg)
++	{
++	    if (comparator== B_ASCIICASEMAP)
++	    {
++		ctag = REG_EXTENDED | REG_NOSUB | REG_ICASE;
++	    }
++	    else
++	    {
++		ctag = REG_EXTENDED | REG_NOSUB;
++	    }
++	}
++
++	/*find the correct comparator fcn*/
++	comp = lookup_comp(comparator, match, relation, &comprock);
++
++	if(!comp) {
++	    res = SIEVE_RUN_ERROR;
++	    break;
++	}
++	
++	/*find the part of the address that we want*/
++	switch(apart)
++	{
++	case B_ALL:
++	    addrpart = ADDRESS_ALL; break;
++	case B_LOCALPART:
++	    addrpart = ADDRESS_LOCALPART; break;
++	case B_DOMAIN:
++	    addrpart = ADDRESS_DOMAIN; break;
++	case B_USER:
++	    addrpart = ADDRESS_USER; break;
++	case B_DETAIL:
++	    addrpart = ADDRESS_DETAIL; break;
++	default:
++	    /* this shouldn't happen with correcct bytecode */
++	    res = SIEVE_RUN_ERROR;
++	}
++
++	if(res == SIEVE_RUN_ERROR) break;
++
++	/*loop through all the headers*/
++	currh=headersi+2;
++#if VERBOSE
++	printf("about to process %d headers\n", numheaders);
++#endif
++	for (x=0; x<numheaders && !res; x++)
++	{
++	    const char *this_header;
++
++	    currh = unwrap_string(bc, currh, &this_header, NULL);
++	    
++	    /* Try the next string if we don't have this one */
++	    if(address) {
++		/* Header */
++		if(interp->getheader(m, this_header, &val) != SIEVE_OK)
++		    continue;
++#if VERBOSE
++                printf(" [%d] header %s is %s\n", x, this_header, val[0]);
++#endif
++	    } else {
++		/* Envelope */
++		if(interp->getenvelope(m, this_header, &val) != SIEVE_OK)
++		    continue;
++	    }
++	
++	    /*header exists, now to test it*/
++	    /*search through all the headers that match*/
++	    
++	    for (y=0; val[y]!=NULL && !res; y++) {
++		
++#if VERBOSE
++		printf("about to parse %s\n", val[y]);
++#endif
++		    
++		if (parse_address(val[y], &data, &marker)!=SIEVE_OK) 
++		    return 0;
++		    
++		while (!res &&
++		       (addr = get_address(addrpart, &data, &marker, 0))) {
++#if VERBOSE
++		    printf("working addr %s\n", (addr ? addr : "[nil]"));
++#endif
++			
++		    if (match == B_COUNT) {
++			count++;
++		    } else {
++			/*search through all the data*/ 
++			currd=datai+2;
++			for (z=0; z<numdata && !res; z++)
++			{
++			    const char *data_val;
++			    
++			    currd = unwrap_string(bc, currd, &data_val, NULL);
++
++			    if (isReg) {
++				reg = bc_compile_regex(data_val, ctag,
++						       errbuf, sizeof(errbuf));
++				if (!reg) {
++				    /* Oops */
++				    res=-1;
++				    goto alldone;
++				}
++
++				res |= comp(val[y], (const char *)reg,
++					    comprock);
++				free(reg);
++			    } else {
++#if VERBOSE
++				printf("%s compared to %s(from script)\n",
++				       addr, data_val);
++#endif 
++				res |= comp(addr, data_val, comprock);
++			    }
++			} /* For each data */
++		    }
++		} /* For each address */
++
++		free_address(&data, &marker);
++	    }/* For each message header */
++	    
++#if VERBOSE
++	    printf("end of loop, res is %d, x is %d (%d)\n", res, x, numheaders);
++#endif	    
++	} /* For each script header */
++     
++	if  (match == B_COUNT)
++	{
++	    sprintf(scount, "%u", count);
++	    /* search through all the data */ 
++	    currd=datai+2;
++	    for (z=0; z<numdata && !res; z++)
++	    {
++		const char *data_val;
++		
++		currd = unwrap_string(bc, currd, &data_val, NULL);
++
++		res |= comp(scount, data_val, comprock);
++	    }
++	}
++
++	/* Update IP */
++	i=(ntohl(bc[datai+1].value)/4);
++	
++	break;
++    }
++    case BC_HEADER:/*9*/
++    {
++	const char** val;
++
++	int headersi=i+4;/*the i value for the begining of hte headers*/
++	int datai=(ntohl(bc[headersi+1].value)/4);
++
++	int numheaders=ntohl(bc[headersi].len);
++	int numdata=ntohl(bc[datai].len);
++
++	int currh, currd; /*current header, current data*/
++
++	int match=ntohl(bc[i+1].value);
++	int relation=ntohl(bc[i+2].value);
++	int comparator=ntohl(bc[i+3].value);
++	int count=0;	
++	char scount[3];
++	int isReg = (match==B_REGEX);
++	int ctag = 0;
++	regex_t *reg;
++	char errbuf[100]; /* Basically unused, regexps tested at compile */ 
++
++	/* set up variables needed for compiling regex */
++	if (isReg)
++	{
++	    if (comparator== B_ASCIICASEMAP)
++	    {
++		ctag= REG_EXTENDED | REG_NOSUB | REG_ICASE;
++	    }
++	    else
++	    {
++		ctag= REG_EXTENDED | REG_NOSUB;
++	    }
++     
++	}
++	
++	/*find the correct comparator fcn*/
++	comp=lookup_comp(comparator, match, relation, &comprock);
++
++	if(!comp) {
++	    res = SIEVE_RUN_ERROR;
++	    break;
++	}
++
++	/*search through all the flags for the header*/
++	currh=headersi+2;
++	for(x=0; x<numheaders && !res; x++)
++	{
++	    const char *this_header;
++	    
++	    currh = unwrap_string(bc, currh, &this_header, NULL);
++	   
++	    if(interp->getheader(m, this_header, &val) != SIEVE_OK) {
++		continue; /*this header does not exist, search the next*/ 
++	    }
++#if VERBOSE
++	    printf ("val %s %s %s\n", val[0], val[1], val[2]);
++#endif
++	    
++	    /* search through all the headers that match */
++	    
++	    for (y=0; val[y]!=NULL && !res; y++)
++	    {
++		if  (match == B_COUNT) {
++		    count++;
++		} else {
++		    /*search through all the data*/ 
++		    currd=datai+2;
++		    for (z=0; z<numdata && !res; z++)
++		    {
++			const char *data_val;
++			
++			currd = unwrap_string(bc, currd, &data_val, NULL);
++			
++			if (isReg) {
++			    reg= bc_compile_regex(data_val, ctag, errbuf,
++						  sizeof(errbuf));
++			    if (!reg)
++			    {
++				/* Oops */
++				res=-1;
++				goto alldone;
++			    }
++			    
++			    res |= comp(val[y], (const char *)reg,
++					comprock);
++			    free(reg);
++			} else {
++			    res |= comp(val[y], data_val, comprock);
++			}
++		    }
++		}
++	    }
++	}
++	
++	if  (match == B_COUNT )
++	{
++	    sprintf(scount, "%u", count);
++	    /*search through all the data*/ 
++	    currd=datai+2;
++	    for (z=0; z<numdata && !res; z++)
++	    { 	
++		const char *data_val;
++			
++		currd = unwrap_string(bc, currd, &data_val, NULL);
++#if VERBOSE
++		printf("%d, %s \n", count, data_val);
++#endif
++		res |= comp(scount, data_val, comprock);
++	    }
++	      
++	}
++
++	/* Update IP */
++	i=(ntohl(bc[datai+1].value)/4);
++	
++	break;
++    }
++    default:
++#if VERBOSE
++	printf("WERT, can't evaluate if statement. %d is not a valid command",
++	       op);
++#endif     
++	return SIEVE_RUN_ERROR;
++    }
++    
++  
++ alldone:
++    
++    *ip=i;
++    return res;
++}
++
++/* The entrypoint for bytecode evaluation */
++int sieve_eval_bc(sieve_interp_t *i, const void *bc_in, unsigned int bc_len,
++		  void *m, sieve_imapflags_t * imapflags,
++		  action_list_t *actions,
++		  notify_list_t *notify_list,
++		  const char **errmsg)
++{
++    const char *data;
++    int ip = 0, ip_max = (bc_len/sizeof(bytecode_input_t));
++    int res=0;
++    int op;
++    int version;
++    
++    bytecode_input_t *bc = (bytecode_input_t *)bc_in;
++    
++    /* Check that we
++     * a) have bytecode
++     * b) it is atleast long enough for the magic number, the version
++     *    and one opcode */
++    if(!bc) return SIEVE_FAIL;
++    if(bc_len < (BYTECODE_MAGIC_LEN + 2*sizeof(bytecode_input_t)))
++       return SIEVE_FAIL;
++
++    if(memcmp(bc, BYTECODE_MAGIC, BYTECODE_MAGIC_LEN)) {
++	*errmsg = "Not a bytecode file";
++	return SIEVE_FAIL;
++    }
++
++    ip = BYTECODE_MAGIC_LEN / sizeof(bytecode_input_t);
++
++    version= ntohl(bc[ip].op);
++
++    /* this is because there was a time where integers were not network byte
++       order.  all the scripts written then would have version 0x01 written
++       in host byte order.*/
++
++     if(version == (int)ntohl(1)) {
++	if(errmsg) {
++	    *errmsg =
++		"Incorrect Bytecode Version, please recompile (use sievec)";
++	    
++	}
++	return SIEVE_FAIL;
++    }
++    
++    if( version != BYTECODE_VERSION) {
++	if(errmsg) {
++	    *errmsg =
++		"Incorrect Bytecode Version, please recompile (use sievec)";
++	}
++	return SIEVE_FAIL;
++    }
++
++#if VERBOSE
++    printf("version number %d\n",version); 
++#endif
++
++    for(ip++; ip<ip_max; ) { 
++	op=ntohl(bc[ip].op);
++	switch(op) {
++	case B_STOP:/*0*/
++	    res=1;
++	    break;
++
++	case B_KEEP:/*1*/
++	    res = do_keep(actions, imapflags);
++	    if (res == SIEVE_RUN_ERROR)
++		*errmsg = "Keep can not be used with Reject";
++	    ip++;
++	    break;
++
++	case B_DISCARD:/*2*/
++	    res=do_discard(actions);
++	    ip++;
++	    break;
++
++	case B_REJECT:/*3*/
++	    ip = unwrap_string(bc, ip+1, &data, NULL);
++	    
++	    res = do_reject(actions, data);
++	
++	    if (res == SIEVE_RUN_ERROR)
++		*errmsg = "Reject can not be used with any other action";  
++
++	    break;
++
++	case B_FILEINTO:/*4*/
++	{
++	    ip = unwrap_string(bc, ip+1, &data, NULL);
++
++	    res = do_fileinto(actions, data, imapflags);
++
++	    if (res == SIEVE_RUN_ERROR)
++		*errmsg = "Fileinto can not be used with Reject";
++
++	    break;
++	}
++
++	case B_REDIRECT:/*5*/
++	{
++	    ip = unwrap_string(bc, ip+1, &data, NULL);
++
++	    res = do_redirect(actions, data);
++
++	    if (res == SIEVE_RUN_ERROR)
++		*errmsg = "Redirect can not be used with Reject";
++
++	    break;
++	}
++
++	case B_IF:/*6*/
++	{
++	    int testend=ntohl(bc[ip+1].value);
++	    int result;
++	   
++	    ip+=2;
++	    result=eval_bc_test(i, m, bc, &ip);
++	    
++	    if (result<0) {
++		*errmsg = "Invalid test";
++		return SIEVE_FAIL;
++	    } else if (result) {
++	    	/*skip over jump instruction*/
++		testend+=2;
++	    }
++	    ip=testend;
++	    
++	    break;
++	}
++
++	case B_MARK:/*8*/
++	    res = do_mark(actions);
++	    ip++;
++	    break;
++
++	case B_UNMARK:/*9*/
++	    res = do_unmark(actions);
++	    ip++;
++	    break;
++
++	case B_ADDFLAG:/*10*/ 
++	{
++	    int x;
++	    int list_len=ntohl(bc[ip+1].len);
++
++	    ip+=3; /* skip opcode, list_len, and list data len */
++
++	    for (x=0; x<list_len; x++) {
++		ip = unwrap_string(bc, ip, &data, NULL);
++		
++		res = do_addflag(actions, data);
++
++		if (res == SIEVE_RUN_ERROR)
++		    *errmsg = "addflag can not be used with Reject";
++	    } 
++	    break;
++	}
++
++	case B_SETFLAG:
++	{
++	    int x;
++	    int list_len=ntohl(bc[ip+1].len);
++
++	    ip+=3; /* skip opcode, list_len, and list data len */
++
++	    ip = unwrap_string(bc, ip, &data, NULL);
++
++	    res = do_setflag(actions, data);
++
++	    if (res == SIEVE_RUN_ERROR) {
++		*errmsg = "setflag can not be used with Reject";
++	    } else {
++		for (x=1; x<list_len; x++) {
++		    ip = unwrap_string(bc, ip, &data, NULL);
++
++		    res = do_addflag(actions, data);
++
++		    if (res == SIEVE_RUN_ERROR)
++			*errmsg = "setflag can not be used with Reject";
++		} 
++	    }
++	    
++	    break;
++	}
++
++	case B_REMOVEFLAG:
++	{
++	    int x;
++	    int list_len=ntohl(bc[ip+1].len);
++
++	    ip+=3; /* skip opcode, list_len, and list data len */
++
++	    for (x=0; x<list_len; x++) {
++		ip = unwrap_string(bc, ip, &data, NULL);
++
++		res = do_removeflag(actions, data);
++
++		if (res == SIEVE_RUN_ERROR)
++		    *errmsg = "removeflag can not be used with Reject";
++	    } 
++	    break;
++	}
++
++	case B_NOTIFY:
++	{
++	    const char * id;
++	    const char * method;
++	    const char **options = NULL;
++	    const char *priority = NULL;
++	    const char * message;
++	    int pri;
++	    
++	    ip++;
++
++	    /* method */
++	    ip = unwrap_string(bc, ip, &method, NULL);
++
++	    /* id */
++	    ip = unwrap_string(bc, ip, &id, NULL);
++
++	    /*options*/
++	    options=bc_makeArray(bc, &ip); 
++
++	    /* priority */
++	    pri=ntohl(bc[ip].value);
++	    ip++;
++	    
++	    switch (pri)
++	    {
++	    case B_LOW:
++		priority="low";
++	    case B_NORMAL:
++		priority="normal";
++		break;
++	    case B_HIGH: 
++		priority="high";
++		break; 
++	    case B_ANY:
++		priority="any";
++		break;
++	    default:
++		res=SIEVE_RUN_ERROR;
++	    }
++
++	    /* message */
++	    ip = unwrap_string(bc, ip, &message, NULL);
++	  
++	    res = do_notify(notify_list, id, method, options,
++			    priority, message);
++
++	    break;
++	}
++	case B_DENOTIFY:
++	{
++         /*
++	  * i really have no idea what the count matchtype should do here.
++	  * the sanest thing would be to use 1.
++	  * however that would require passing on the match type to do_notify.
++	  *  -jsmith2
++	  */
++
++	    comparator_t *comp = NULL;
++	    
++	    const char *pattern;
++	    regex_t *reg;
++	    
++	    const char *priority = NULL;
++	    void *comprock = NULL;
++	    
++	    int comparator;
++	    int pri;
++	    
++	    ip++;
++	    pri=ntohl(bc[ip].value);
++	    ip++;
++	    
++	    switch (pri)
++	    {
++	    case B_LOW:
++		priority="low";		
++	    case B_NORMAL:
++		priority="normal";
++		break;
++	    case B_HIGH: 
++		priority="high";
++		break; 
++	    case B_ANY:
++		priority="any";
++		break;
++	    default:
++		res=SIEVE_RUN_ERROR;
++	    }
++
++	    if(res == SIEVE_RUN_ERROR)
++		break;
++	   
++	    comparator =ntohl( bc[ip].value);
++	    ip++;
++	    
++	    if (comparator == B_ANY)
++	    { 
++		ip++;/* skip placeholder this has no comparator function */
++		comp=NULL;
++	    } else {
++		int x= ntohl(bc[ip].value);
++		ip++;
++		
++		comp=lookup_comp(B_ASCIICASEMAP,comparator,
++				 x, &comprock);
++	    }
++	    
++	    ip = unwrap_string(bc, ip, &pattern, NULL);
++	  
++	    if (comparator == B_REGEX)
++	    {	
++		char errmsg[1024]; /* Basically unused */
++		
++		reg=bc_compile_regex(pattern,
++				     REG_EXTENDED | REG_NOSUB | REG_ICASE,
++				     errmsg, sizeof(errmsg));
++		if (!reg) {
++		    res = SIEVE_RUN_ERROR;
++		} else {
++		    res = do_denotify(notify_list, comp, reg,
++				      comprock, priority);
++		    free(reg);
++		}
++	    } else {
++		res = do_denotify(notify_list, comp, pattern,
++				  comprock, priority);
++	    }
++	    
++	    break;
++	}
++	case B_VACATION:
++	{
++	    int respond;
++	    char *fromaddr = NULL; /* relative to message we send */
++	    char *toaddr = NULL; /* relative to message we send */
++	    const char *message = NULL;
++	    char buf[128];
++	    char subject[1024];
++	    int x;
++	    
++	    ip++;
++
++	    x=ntohl( bc[ip].len);
++	    
++	    respond=shouldRespond(m, i, x, bc, ip+2,
++				  &fromaddr, &toaddr);
++	    
++	    ip=(ntohl(bc[ip+1].value)/4);	
++	    if (respond==SIEVE_OK)
++	    {	 
++		ip = unwrap_string(bc, ip, &data, NULL);
++		
++		if (!data) 
++		{
++		    /* we have to generate a subject */
++		    const char **s;	    
++		    strlcpy(buf, "subject", sizeof(buf));
++		    if (i->getheader(m, buf, &s) != SIEVE_OK ||
++			s[0] == NULL) {
++			strlcpy(subject, "Automated reply", sizeof(subject));
++		    } else {
++			/* s[0] contains the original subject */
++			const char *origsubj = s[0];
++
++			while (!strncasecmp(origsubj, "Re: ", 4)) 
++			    origsubj += 4;
++
++			snprintf(subject, sizeof(subject), "Re: %s", origsubj);
++		    }
++		} else {
++		    /* user specified subject */
++		    strlcpy(subject, data, sizeof(subject));
++		}
++		
++		ip = unwrap_string(bc, ip, &message, NULL);
++
++		res = do_vacation(actions, toaddr, fromaddr,
++				  xstrdup(subject), message,
++				  ntohl(bc[ip].value), ntohl(bc[ip+1].value));
++
++		ip+=2;		
++
++		if (res == SIEVE_RUN_ERROR)
++		    *errmsg = "Vacation can not be used with Reject or Vacation";
++	    } else if (respond == SIEVE_DONE) {
++                /* skip subject and message */
++
++		ip = unwrap_string(bc, ip, &data, NULL);
++		ip = unwrap_string(bc, ip, &data, NULL);
++
++		ip+=2;/*skip days and mime flag*/
++	    } else {
++		res = SIEVE_RUN_ERROR; /* something is bad */ 
++	    }
++
++	    break;
++	}
++	case B_NULL:/*15*/
++	    ip++;
++	    break;
++
++	case B_JUMP:/*16*/
++	    ip= ntohl(bc[ip+1].jump);
++	    break;
++	    
++	default:
++	    if(errmsg) *errmsg = "Invalid sieve bytecode";
++	    return SIEVE_FAIL;
++	}
++      
++	if (res) /* we've either encountered an error or a stop */
++	    break;
++    }
++    return res;      
++}
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/bc_generate.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/bc_generate.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,708 @@
++/* bc_generate.c -- sieve bytecode- almost flattened bytecode
++ * Rob Siemborski
++ * $Id$
++ */
++/***********************************************************
++        Copyright 2001 by Carnegie Mellon University
++
++                      All Rights Reserved
++
++Permission to use, copy, modify, and distribute this software and its
++documentation for any purpose and without fee is hereby granted,
++provided that the above copyright notice appear in all copies and that
++both that copyright notice and this permission notice appear in
++supporting documentation, and that the name of Carnegie Mellon
++University not be used in advertising or publicity pertaining to
++distribution of the software without specific, written prior
++permission.
++
++CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
++FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
++ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
++OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++******************************************************************/
++
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include "xmalloc.h"
++#include "sieve_interface.h"
++
++#include "script.h"
++#include "tree.h"
++#include "sieve.h"
++
++#include "bytecode.h"
++
++#include <assert.h>
++#include <string.h>
++
++
++
++struct bytecode_info 
++{
++    bytecode_t *data;/* pointer to almost-flat bytecode */
++    size_t scriptend; /* used by emit code to know final length of bytecode */
++    size_t reallen; /* allocated length of 'data' */
++};
++
++static int bc_test_generate(int codep, bytecode_info_t *retval, test_t *t);
++
++/* returns false if the request can't be satisfied, true if it can. */
++
++static int atleast(bytecode_info_t *arr, size_t len) 
++{
++    if(arr->reallen < len) {
++	/* too small; double if that's big enough, otherwise increase to the
++	   requested size. */
++	arr->reallen = (len > arr->reallen * 2 ? len : arr->reallen * 2);
++	arr->data = xrealloc(arr->data, arr->reallen*sizeof(bytecode_t));
++	if(!arr->data) 
++	{ /* out of memory? */
++	    return 0;
++	}
++    }
++    
++    return 1;
++}
++
++/*
++ * functions of the form bc_XXX_generate have the following properties:
++ * on success they return an int that corresponds to the next empty location
++ * for code, and on failure they return -1.
++ *
++ *  they will take a  bytecode_info_t as a parameter and modify it by
++ *  making it larger and adding more bytecommands in the pass 1 form
++ */
++
++/* given a location and a string list, compile it into almost-flat form.
++ * <list len> <string len><string ptr><string len><string ptr> etc... */
++static int bc_stringlist_generate(int codep, bytecode_info_t *retval,
++				  stringlist_t *sl) 
++{
++    int len_codep = codep;
++    int strcount = 0;
++    stringlist_t *cur;
++    
++    codep++;
++
++    /* Bounds check the string list length */
++    if(!atleast(retval,codep+1)) 
++	return -1;
++
++    for(cur=sl; cur; cur=cur->next) 
++    {
++	strcount++;
++	assert((cur->s)!=NULL);
++	
++	/* Bounds check for each string before we allocate it */
++	if(!atleast(retval,codep+2)) 
++	    return -1;
++
++	retval->data[codep++].len = strlen(cur->s);
++	retval->data[codep++].str = cur->s;
++    }
++    
++    retval->data[len_codep].listlen = strcount;
++    return codep;
++}
++
++
++/* write a list of tests into almost-flat form, starting at codep.
++ * returns the next code location, -1 on error. */
++
++/* <list len> <next test ptr> <test ...> <next test ptr> <test ...> ... */
++static int bc_testlist_generate(int codep, bytecode_info_t *retval, 
++				testlist_t *tl) 
++{
++    int len_codep = codep;
++    int testcount = 0;
++    testlist_t *cur;
++    
++    codep++;
++
++    /* Bounds check the test list length */
++    if(!atleast(retval,codep+1)) 
++	return -1;
++       
++    for(cur=tl; cur; cur=cur->next) {
++	int oldcodep = codep;
++
++	/* Make room for tail marker */
++	if(!atleast(retval,codep+1)) 
++	    return -1;
++
++	testcount++;
++	codep = bc_test_generate(codep+1, retval, cur->t);
++
++	retval->data[oldcodep].jump = codep;
++    }
++
++    retval->data[len_codep].listlen = testcount;
++        
++    return codep;
++}
++/* output a relation into almost-flat form at codep.
++ * returns new codep on success, -1 on failure. */
++static int bc_relation_generate(int codep, bytecode_info_t *retval, int relat)
++{
++    if (!atleast(retval, codep + 1)) return -1;
++    switch (relat)
++    {
++    case GT:
++	retval->data[codep++].value= B_GT;
++	break;
++    case GE:
++	retval->data[codep++].value= B_GE;
++	break; 
++    case LT:
++	retval->data[codep++].value= B_LT;
++	break;
++    case LE:
++	retval->data[codep++].value= B_LE;
++	break;
++    case EQ:
++	retval->data[codep++].value= B_EQ;
++	break;
++    case NE:
++	retval->data[codep++].value= B_NE;
++	break;
++    default:
++	/* comparator has no relational field */
++	retval->data[codep++].value=  -1;
++	break;
++    }
++    return codep;
++}
++/* writes a single comparator into almost-flat form starting at codep.
++ * will always write out 3 words
++ * returns the next code location or -1 on error. */
++static int bc_comparator_generate(int codep, bytecode_info_t *retval,
++                                  int comptag, int relat,
++                                  const char *comparator)
++{
++    assert(retval != NULL);
++
++    /* comptag */
++    if (!atleast(retval, codep + 1)) return -1;
++
++    switch (comptag) {
++    case IS:
++        retval->data[codep++].value = B_IS;
++        break;
++    case CONTAINS:
++        retval->data[codep++].value = B_CONTAINS;
++        break;
++    case MATCHES:
++        retval->data[codep++].value = B_MATCHES;
++        break;
++#ifdef ENABLE_REGEX
++    case REGEX:
++        retval->data[codep++].value = B_REGEX;
++        break;
++#endif
++    case COUNT:
++        retval->data[codep++].value = B_COUNT;
++        break;
++    case VALUE:
++        retval->data[codep++].value = B_VALUE;
++        break;
++
++    default:
++        return -1;
++    }
++  
++    /*relation*/
++    codep = bc_relation_generate(codep, retval, relat);
++  
++    /* comparator (value specified with :comparator) */
++    if (!atleast(retval, codep + 1)) return -1;
++  
++    /* xxx perhaps extend comparator.h to have
++       lookup_comp return an index, and then
++       lookup_by_index return the actual comparator?
++       
++       we can then eliminate the comptag above, too. */
++    
++    if (!strcmp (comparator, "i;octet"))
++        retval->data[codep++].value = B_OCTET;
++    else if (!strcmp (comparator, "i;ascii-casemap"))
++        retval->data[codep++].value = B_ASCIICASEMAP;
++    else if (!strcmp (comparator, "i;ascii-numeric"))
++        retval->data[codep++].value = B_ASCIINUMERIC;
++
++    return codep;
++}
++
++
++
++/* writes a single test into almost-flat form starting at codep.
++ * returns the next code location or -1 on error. */
++static int bc_test_generate(int codep, bytecode_info_t *retval, test_t *t)
++{
++    if(!retval) return -1;
++    switch(t->type) {
++    case STRUE: /* BC_TRUE */
++	if(!atleast(retval,codep+1)) return -1;
++	retval->data[codep++].op = BC_TRUE;
++	break;
++    case SFALSE:/* BC_FALSE */
++	if(!atleast(retval,codep+1)) return -1;
++	retval->data[codep++].op = BC_FALSE;
++	break;
++    case NOT: /* BC_NOT {subtest : test} */
++	if(!atleast(retval,codep+1)) return -1;
++	retval->data[codep++].op = BC_NOT;
++	codep = bc_test_generate(codep, retval, t->u.t);
++	if (codep == -1) return -1;
++	break;
++    case SIZE: /* BC_SIZE (B_OVER | B_UNDER) {size : int} */
++	if(!atleast(retval,codep+3)) return -1;
++	retval->data[codep++].op = BC_SIZE;
++	retval->data[codep++].value = (t->u.sz.t == OVER
++				       ? B_OVER : B_UNDER);
++	retval->data[codep++].value = t->u.sz.n;
++	break;
++    case EXISTS:/* BC_EXISTS { headers : string list } */
++	if(!atleast(retval,codep+1)) return -1;
++	retval->data[codep++].op = BC_EXISTS;
++	codep= bc_stringlist_generate(codep, retval, t->u.sl);
++	break;
++    case ANYOF:/* BC_ANYOF { tests : test list } */
++	if(!atleast(retval,codep+1)) return -1;
++	retval->data[codep++].op = BC_ANYOF;
++	codep=bc_testlist_generate(codep, retval, t->u.tl);
++	if (codep == -1) return -1;
++	break;
++    case ALLOF: /* BC_ALLOF { tests : test list } */
++	if(!atleast(retval,codep+1)) return -1;
++	retval->data[codep++].op = BC_ALLOF;
++	codep= bc_testlist_generate(codep, retval, t->u.tl);
++	if (codep == -1) return -1;
++	break;
++    case HEADER:
++	/* BC_HEADER { c: comparator } { headers : string list }
++	   { patterns : string list } 
++	*/
++      
++	if(!atleast(retval,codep + 1)) return -1;
++	retval->data[codep++].op = BC_HEADER;
++      
++	/* comparator */
++	codep = bc_comparator_generate(codep, retval,
++				       t->u.h.comptag,
++				       t->u.h.relation,
++				       t->u.h.comparator);
++	if (codep == -1) return -1;
++      
++	/* headers */
++	codep = bc_stringlist_generate(codep, retval, t->u.h.sl);
++	if (codep == -1) return -1;
++      
++	/* pattern */
++	codep = bc_stringlist_generate(codep, retval, t->u.h.pl);
++	if (codep == -1) return -1;
++	break;
++    case ADDRESS:
++    case ENVELOPE:
++	/* (BC_ADDRESS | BC_ENVELOPE) {c : comparator} 
++	   (B_ALL | B_LOCALPART | ...) { header : string list }
++	   { pattern : string list } */
++      
++	if(!atleast(retval,codep+1)) return -1;
++      
++	retval->data[codep++].op = (t->type == ADDRESS)
++	    ? BC_ADDRESS : BC_ENVELOPE;
++            
++	codep = bc_comparator_generate(codep, retval,t->u.ae.comptag,
++				       t->u.ae.relation, 
++				       t->u.ae.comparator);
++	if (codep == -1) return -1;
++
++	if(!atleast(retval,codep+1)) return -1;
++
++	/*address part*/
++	switch(t->u.ae.addrpart) {
++	case ALL:
++	    retval->data[codep++].value = B_ALL;
++	    break;
++	case LOCALPART:
++	    retval->data[codep++].value = B_LOCALPART;
++	    break;
++	case DOMAIN:
++	    retval->data[codep++].value = B_DOMAIN;
++	    break;
++	case USER:
++	    retval->data[codep++].value = B_USER;
++	    break;
++	case DETAIL:
++	    retval->data[codep++].value = B_DETAIL;
++	    break;
++	default:
++	    return -1;
++	}
++
++	/*headers*/
++	codep = bc_stringlist_generate(codep, retval, t->u.ae.sl);
++	if (codep == -1) return -1;
++
++	/*patterns*/
++	codep = bc_stringlist_generate(codep, retval, t->u.ae.pl);
++	if (codep == -1) return -1;
++     
++	break;
++    default:
++	return -1;
++      
++    }
++    return codep;
++}
++
++
++/* generate a not-quite-flattened bytecode */
++/* returns address of next instruction or -1 on error*/
++/* needs current instruction, buffer for the code, and a current parse tree */
++/* sieve is cool because everything is immediate! */
++static int bc_action_generate(int codep, bytecode_info_t *retval,
++			      commandlist_t *c) 
++{
++    int jumploc,baseloc;
++
++    if(!retval) return -1;
++    if (c==NULL)
++    {
++	if(!atleast(retval,codep+1)) return -1;
++	retval->data[codep++].op = B_NULL;
++    }
++    else
++    {
++	do {
++	    switch(c->type) {
++	    case STOP:
++		/* STOP (no arguments) */
++		if(!atleast(retval,codep+1)) return -1;
++		retval->data[codep++].op = B_STOP;
++		break;
++	    case DISCARD:
++		/* DISCARD (no arguments) */
++		if(!atleast(retval,codep+1)) return -1;
++		retval->data[codep++].op = B_DISCARD;
++		break;
++	    case KEEP:
++		/* KEEP (no arguments) */
++		if(!atleast(retval,codep+1)) return -1;
++		retval->data[codep++].op = B_KEEP;
++		break;
++	    case MARK:
++		/* MARK (no arguments) */
++		if(!atleast(retval,codep+1)) return -1;
++		retval->data[codep++].op = B_MARK;
++		break;
++	    case UNMARK:
++		/* UNMARK (no arguments) */
++		if(!atleast(retval,codep+1)) return -1;
++		retval->data[codep++].op = B_UNMARK;
++		break;
++	    case DENOTIFY:
++		/* DENOTIFY  */
++		if(!atleast(retval,codep+6)) return -1;
++		retval->data[codep++].op = B_DENOTIFY;
++		switch(c->u.d.priority) {
++		case LOW:
++		    retval->data[codep++].value = B_LOW;
++		    break;
++		case NORMAL:
++		    retval->data[codep++].value = B_NORMAL;
++		    break;
++		case HIGH:
++		    retval->data[codep++].value = B_HIGH;
++		    break;
++		case ANY:
++		    retval->data[codep++].value = B_ANY;
++		    break;
++		default:
++		    return -1;
++		}
++		switch(c->u.d.comptag) {
++		case IS:
++		    retval->data[codep++].value = B_IS;
++		    break;
++		case CONTAINS:
++		    retval->data[codep++].value = B_CONTAINS;
++		    break;
++		case MATCHES:
++		    retval->data[codep++].value = B_MATCHES;
++		    break;
++#ifdef ENABLE_REGEX
++		case REGEX:
++		    retval->data[codep++].value = B_REGEX;
++		    break;
++#endif
++		case ANY:
++		    retval->data[codep++].value = B_ANY;
++		    break; 
++		default:
++		    return -1;
++		}
++		codep = bc_relation_generate(codep, retval, c->u.d.relation);
++	
++		if(c->u.d.pattern)
++		{
++		    retval->data[codep++].len = strlen(c->u.d.pattern);
++		    retval->data[codep++].str = c->u.d.pattern;
++		} else {
++		    retval->data[codep++].len = -1;
++		    retval->data[codep++].str = NULL;
++		}
++
++		break;
++	    case REJCT:
++		/* REJECT (STRING: len + dataptr) */
++		if(!atleast(retval,codep+3)) return -1;
++		retval->data[codep++].op = B_REJECT;
++		retval->data[codep++].len = strlen(c->u.str);
++		retval->data[codep++].str = c->u.str;
++		break;
++	    case FILEINTO:
++		/* FILEINTO (STRING: len + dataptr) */
++		if(!atleast(retval,codep+3)) return -1;
++		retval->data[codep++].op = B_FILEINTO;
++		retval->data[codep++].len = strlen(c->u.str);
++		retval->data[codep++].str = c->u.str;
++		break;
++	    case REDIRECT:
++		/* REDIRECT (STRING: len + dataptr) */
++		if(!atleast(retval,codep+3)) return -1;
++		retval->data[codep++].op = B_REDIRECT;
++		retval->data[codep++].len = strlen(c->u.str);
++		retval->data[codep++].str = c->u.str;
++		break;
++	    case ADDFLAG:
++		/* ADDFLAG stringlist */
++		if(!atleast(retval,codep+1)) return -1;
++		retval->data[codep++].op = B_ADDFLAG;
++		codep = bc_stringlist_generate(codep,retval,c->u.sl);
++
++		if(codep == -1) return -1;
++		break;
++	    case SETFLAG:
++		/* SETFLAG stringlist */
++		if(!atleast(retval,codep+1)) return -1;
++		retval->data[codep++].op = B_SETFLAG;
++		codep = bc_stringlist_generate(codep,retval,c->u.sl);
++
++		if(codep == -1) return -1;
++		break;
++	    case REMOVEFLAG:
++		/* REMOVEFLAG stringlist */
++		if(!atleast(retval,codep+1)) return -1;
++		retval->data[codep++].op = B_REMOVEFLAG;
++		codep = bc_stringlist_generate(codep,retval,c->u.sl);
++
++		if(codep == -1) return -1;
++		break;
++	    case NOTIFY:
++		/* NOTIFY 
++		   (STRING: len + dataptr)
++		   (STRING: len + dataptr)
++		   stringlist
++		   (STRING: len + dataptr)
++		   (STRING: len + dataptr)
++		   method/id /options list/priority/message 
++		*/
++			
++		if(!atleast(retval,codep+5)) return -1;
++		retval->data[codep++].op = B_NOTIFY;
++		
++		retval->data[codep++].len = strlen(c->u.n.method);
++		retval->data[codep++].str = c->u.n.method;
++				
++		if (c->u.n.id)
++		{
++		    retval->data[codep++].len = strlen(c->u.n.id);
++		    retval->data[codep++].str = c->u.n.id;
++		}
++		else
++		{
++		    retval->data[codep++].len = -1;
++		    retval->data[codep++].str = NULL;
++		}
++		
++		codep = bc_stringlist_generate(codep,retval,c->u.n.options);
++		if(codep == -1) return -1;
++
++		if(!atleast(retval,codep+3)) return -1;
++
++		switch(c->u.n.priority) {
++		case LOW:
++		    retval->data[codep++].value = B_LOW;
++		    break;
++		case NORMAL:
++		    retval->data[codep++].value = B_NORMAL;
++		    break;
++		case HIGH:
++		    retval->data[codep++].value = B_HIGH;
++		    break;
++		case ANY:
++		    retval->data[codep++].value = B_ANY;
++		    break;
++		default:
++		    return -1;
++		}
++		
++		retval->data[codep++].len = strlen(c->u.n.message);
++		retval->data[codep++].str = c->u.n.message;
++		break;
++	    case VACATION:
++		/* VACATION
++		   STRINGLIST addresses
++		   STRING subject (if len is -1, then subject was NULL)
++		   STRING message (again, len == -1 means subject was NULL)
++		   VALUE days
++		   VALUE mime
++		*/
++
++		if(!atleast(retval,codep+1)) return -1;
++		retval->data[codep++].op = B_VACATION;
++	    
++		codep = bc_stringlist_generate(codep,retval,c->u.v.addresses);
++		if (codep == -1) return -1;
++
++		if (!atleast(retval,codep+2)) return -1;
++		if(c->u.v.subject) {
++		    retval->data[codep++].len = strlen(c->u.v.subject);
++		    retval->data[codep++].str = c->u.v.subject;
++		} else {
++		    retval->data[codep++].len = -1;
++		    retval->data[codep++].str = NULL;
++		}
++
++		if (!atleast(retval,codep+2)) return -1;
++		if(c->u.v.message) {
++		    retval->data[codep++].len = strlen(c->u.v.message);
++		    retval->data[codep++].str = c->u.v.message;
++		} else {
++		    retval->data[codep++].len = -1;
++		    retval->data[codep++].str = NULL;
++		}
++
++		if (!atleast(retval,codep+2)) return -1;
++		retval->data[codep++].value = c->u.v.days;
++		retval->data[codep++].value = c->u.v.mime;
++	    
++
++		if(codep == -1) return -1;
++		break;
++	    case IF:
++	    {
++		int jumpVal; 	    
++		/* IF
++		   (int: begin then block)
++		   (int: end then block/begin else block)
++		   (int:end else block) (-1 if no else block)
++		   (test)
++		   (then block)
++		   (else block)(optional)
++		*/
++		baseloc = codep;
++	    
++		/* Allocate operator + jump table offsets */
++		if(!atleast(retval,codep+4)) return -1;
++		
++		jumploc = codep+4;
++		retval->data[codep++].op = B_IF;
++		    
++		/* begining of then  code */
++		jumpVal= bc_test_generate(jumploc,retval,c->u.i.t);
++		if(jumpVal == -1) 
++		    return -1;
++		else {
++		    retval->data[codep].jump = jumpVal;
++		    codep++;
++		}
++	    
++		/* find then code and offset to else code,
++		 * we want to write this code starting at the offset we
++		 * just found */
++	
++		jumpVal= bc_action_generate(jumpVal,retval, c->u.i.do_then);
++		if(jumpVal == -1) 
++		    return -1;
++		else 
++		    retval->data[codep].jump = jumpVal;
++		
++		codep++;
++		/* write else code if its there*/
++		if(c->u.i.do_else) {
++	
++		    jumpVal= bc_action_generate(jumpVal,retval, c->u.i.do_else);
++		    if(jumpVal == -1) 
++		    {
++			return -1;
++		    } else 
++		    {
++			retval->data[codep].jump = jumpVal;
++		    }
++		    
++		    /* Update code pointer to end of else code */
++		    codep = retval->data[codep].jump;
++		} else {
++		    /*there is no else block, so its -1*/
++		    retval->data[codep].jump = -1;
++		    /* Update code pointer to end of then code */
++		    codep = retval->data[codep-1].jump;
++		}
++	    
++		break;
++	    }
++	    default:
++		/* no such action known */
++		return -1;
++	    }
++	  
++	    /* generate from next command */
++	    c = c->next;
++	} while(c);
++    }
++    /*scriptend may be updated before the end, but it will be updated at the end, which is what matters.*/
++    retval->scriptend=codep;
++    return codep;
++   
++}
++
++
++
++/* Entry point to the bytecode emitter module */	
++int sieve_generate_bytecode(bytecode_info_t **retval, sieve_script_t *s) 
++{
++    commandlist_t *c;
++
++    if(!retval) return -1;
++    if(!s) return -1;
++    c = s->cmds;
++    /* if c is NULL, it is handled in bc_action_generate and a script
++       with only BC_NULL is returned
++    */
++
++    
++    *retval = xmalloc(sizeof(bytecode_info_t));
++    if(!(*retval)) return -1;
++
++    memset(*retval, 0, sizeof(bytecode_info_t));
++
++    return bc_action_generate(0, *retval, c);
++}
++
++
++void sieve_free_bytecode(bytecode_info_t **p) 
++{
++    if(!p || !*p) return;
++    if((*p)->data) free((*p)->data);
++    free(*p);
++    *p = NULL;
++}
++ 
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/bytecode.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/bytecode.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,193 @@
++/* bytecode.h -- bytecode definition
++ */
++/***********************************************************
++        Copyright 1999 by Carnegie Mellon University
++
++                      All Rights Reserved
++
++Permission to use, copy, modify, and distribute this software and its
++documentation for any purpose and without fee is hereby granted,
++provided that the above copyright notice appear in all copies and that
++both that copyright notice and this permission notice appear in
++supporting documentation, and that the name of Carnegie Mellon
++University not be used in advertising or publicity pertaining to
++distribution of the software without specific, written prior
++permission.
++
++CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
++FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
++ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
++OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++*****************************************************************/
++
++#ifndef SIEVE_BYTECODE_H
++#define SIEVE_BYTECODE_H
++
++
++/* for debugging*/
++#define DUMPCODE 0
++#define VERBOSE 0
++
++/*for finding correctly aligned bytes on strings*/
++/* bump to the next multiple of 4 bytes */
++#define ROUNDUP(num) (((num) + 3) & 0xFFFFFFFC)
++
++
++/* yes, lots of these are superfluous, it's for clarity */
++typedef union 
++{
++    int op; /* OPTYPE */
++    int value;
++
++    int jump;
++
++    int listlen;
++
++    /* store strings (need 2 consecutive bytecodes) */
++    int len;
++    char *str;
++} bytecode_t;
++
++/* For sanity during input on 64-bit platforms.
++ * str should only be accessed as (char *)&str, but given the use of
++ * unwrap_string, this should be OK */
++typedef union 
++{
++    int op; /* OPTYPE */
++    int value;
++
++    int jump;
++
++    int listlen;
++
++    /* store strings (need 2 consecutive bytecodes) */
++    int len;
++    int str;
++} bytecode_input_t;
++
++
++   /*version 0x01 scripts were written in host byte order.
++   we don't want to use this version number again and cause a mess
++   this isn't a huge concern, since this is version ntohl(1), or 16777216*/
++#define BYTECODE_VERSION 0x03
++#define BYTECODE_MAGIC "CyrSBytecode"
++#define BYTECODE_MAGIC_LEN 12 /* Should be multiple of 4 */
++
++/* IMPORTANT: To maintain forward compatibility of bytecode, please only add
++ * new instructions to the end of these enums.  (The reason these values
++ * are all duplicated here is to avoid silliness if this caveat is forgotten
++ * about in the other tables.) */
++enum bytecode {
++    B_STOP,
++
++    B_KEEP,
++    B_DISCARD,
++    B_REJECT,/* require reject*/
++    B_FILEINTO,/*require fileinto*/
++    B_REDIRECT,
++
++    B_IF,
++  
++    B_MARK,/* require imapflags */
++    B_UNMARK,/* require imapflags */
++
++    B_ADDFLAG,/* require imapflags */
++    B_SETFLAG,/* require imapflags */
++    B_REMOVEFLAG,/* require imapflags */
++
++    B_NOTIFY,/* require notify */
++    B_DENOTIFY,/* require notify */
++
++    B_VACATION,/* require vacation*/
++    B_NULL,
++    B_JUMP
++};
++
++enum bytecode_comps {
++    BC_FALSE,
++    BC_TRUE,
++    BC_NOT,
++    BC_EXISTS,
++    BC_SIZE,
++    BC_ANYOF,
++    BC_ALLOF,
++    BC_ADDRESS,
++    BC_ENVELOPE,  /* require envelope*/
++    BC_HEADER    
++};
++
++/* currently one enum so as to help determine where values are being misused.
++ * we have left placeholders incase we need to add more later to the middle */
++enum bytecode_tags {
++    /* Sizes */
++    B_OVER,
++    B_UNDER,
++
++    B_SIZE_PLACEHOLDER_1,
++    B_SIZE_PLACEHOLDER_2,
++     
++    /* sizes, pt 2 */
++    B_GT, /* require relational*/
++    B_GE,  /* require relational*/
++    B_LT,  /* require relational*/
++    B_LE,  /* require relational*/
++    B_EQ,  /* require relational*/
++    B_NE,  /* require relational*/
++ 
++    B_RELATIONAL_PLACEHOLDER_1,
++    B_RELATIONAL_PLACEHOLDER_2,
++   
++    /* priorities */
++    B_LOW,
++    B_NORMAL,
++    B_HIGH,
++    B_ANY,
++
++    B_PRIORITY_PLACEHOLDER_1,
++    B_PRIORITY_PLACEHOLDER_2,
++    B_PRIORITY_PLACEHOLDER_3,
++    B_PRIORITY_PLACEHOLDER_4,
++    
++    /* Address Part Tags */
++    B_ALL,
++    B_LOCALPART,
++    B_DOMAIN,
++    B_USER,  /* require subaddress */
++    B_DETAIL, /* require subaddress */
++    
++    B_ADDRESS_PLACEHOLDER_1,
++    B_ADDRESS_PLACEHOLDER_2,
++    B_ADDRESS_PLACEHOLDER_3,
++    B_ADDRESS_PLACEHOLDER_4,
++
++    /* Comparators */
++    B_ASCIICASEMAP,
++    B_OCTET,
++    B_ASCIINUMERIC, /* require comparator-i;ascii-numeric */
++    
++    B_COMPARATOR_PLACEHOLDER_1,
++    B_COMPARATOR_PLACEHOLDER_2,
++    B_COMPARATOR_PLACEHOLDER_3,
++    B_COMPARATOR_PLACEHOLDER_4,
++ 
++    /* match types */
++    B_IS,
++    B_CONTAINS,
++    B_MATCHES,
++    B_REGEX,/* require regex*/
++    B_COUNT,/* require relational*/
++    B_VALUE,/* require relational*/
++
++    B_MATCH_PLACEHOLDER_1,
++    B_MATCH_PLACEHOLDER_2,
++    B_MATCH_PLACEHOLDER_3,
++    B_MATCH_PLACEHOLDER_4
++  
++};
++
++int unwrap_string(bytecode_input_t *bc, int pos, const char **str, int *len);
++
++#endif
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/comparator.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/comparator.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,448 @@
++/* comparator.c -- comparator functions
++ * Larry Greenfield
++ * $Id$
++ */
++/***********************************************************
++        Copyright 1999 by Carnegie Mellon University
++
++                      All Rights Reserved
++
++Permission to use, copy, modify, and distribute this software and its
++documentation for any purpose and without fee is hereby granted,
++provided that the above copyright notice appear in all copies and that
++both that copyright notice and this permission notice appear in
++supporting documentation, and that the name of Carnegie Mellon
++University not be used in advertising or publicity pertaining to
++distribution of the software without specific, written prior
++permission.
++
++CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
++FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
++ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
++OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++******************************************************************/
++
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include <stdlib.h>
++#include <ctype.h>
++#include <string.h>
++
++#include "xmalloc.h"
++#include "comparator.h"
++#include "tree.h"
++#include "sieve.h"
++#include "bytecode.h"
++
++/*!!! uses B_CONTAINS not CONTAINS, etc, only works with bytecode*/
++
++extern int strcasecmp(const char *, const char *);
++
++typedef int (*compare_t)(const void *, const void *);
++
++/* --- relational comparators --- */
++
++/* these are generic wrappers in which 'rock' is the compare function */
++
++static int rel_eq(const char *text, const char *pat, void *rock)
++{
++    compare_t compar = (compare_t) rock;
++
++    return (compar(text, pat) == 0);
++}
++
++static int rel_ne(const char *text, const char *pat, void *rock)
++{
++    compare_t compar = (compare_t) rock;
++
++    return (compar(text, pat) != 0);
++}
++
++static int rel_gt(const char *text, const char *pat, void *rock)
++{
++    compare_t compar = (compare_t) rock;
++
++    return (compar(text, pat) > 0);
++}
++
++static int rel_ge(const char *text, const char *pat, void *rock)
++{
++    compare_t compar = (compare_t) rock;
++
++    return (compar(text, pat) >= 0);
++}
++
++static int rel_lt(const char *text, const char *pat, void *rock)
++{
++    compare_t compar = (compare_t) rock;
++
++    return (compar(text, pat) < 0);
++}
++
++static int rel_le(const char *text, const char *pat, void *rock)
++{
++    compare_t compar = (compare_t) rock;
++
++    return (compar(text, pat) <= 0);
++}
++
++/* --- i;octet comparators --- */
++
++/* just compare the two; these should be NULL terminated */
++static int octet_cmp(const char *text, const char *pat)
++{
++    size_t sl;
++    int r;
++
++    sl = strlen(text) < strlen(pat) ? strlen(text) : strlen(pat);
++
++    r = memcmp(text, pat, sl);
++
++    if (r == 0)
++	return (strlen(text) - strlen(pat));
++    else 
++	return r;
++}
++
++/* we implement boyer-moore for hell of it, since this is probably
++ not very useful for sieve */
++#if 0
++int boyer_moore(char *text, char *pat)
++{
++    int i, j; /* indexes */
++    int M = strlen(pat); /* length of pattern */
++    int N = strlen(text); /* length of text */
++    int skip[256]; /* table of how much to skip, based on each character */
++
++    /* initialize skip table */
++    for (i = 0; i < 256; i++)
++	skip[i] = M;
++    for (i = 0; i < M; i++)
++	skip[(int) pat[i]] = M-i-1;
++    
++    /* look for pat in text */
++    i = j = M-1;
++    do {
++	if (pat[j] == text[i]) {
++	    i--;
++	    j--;
++	} else {
++	    if (M-j > skip[(int) text[i]]) {
++		i = i + M - j;
++	    } else {
++		i = i + skip[(int) text[i]];
++	    }
++	    j = M-1;
++	}
++    } while (!((j < 0) || (i >= N)));
++    /* i+1 is the position of the match if i < N */
++    return (i < N) ? 1 : 0;
++}
++#endif
++
++/* we do a brute force attack */
++static int octet_contains(const char *text, const char *pat, 
++                          void *rock __attr_unused__)
++{
++    return (strstr(text, pat) != NULL);
++}
++
++static int octet_matches_(const char *text, const char *pat, int casemap)
++{
++    const char *p;
++    const char *t;
++    char c;
++
++    t = text;
++    p = pat;
++    for (;;) {
++	if (*p == '\0') {
++	    /* ran out of pattern */
++	    return (*t == '\0');
++	}
++	c = *p++;
++	switch (c) {
++	case '?':
++	    if (*t == '\0') {
++		return 0;
++	    }
++	    t++;
++	    break;
++	case '*':
++	    while (*p == '*' || *p == '?') {
++		if (*p == '?') {
++		    /* eat the character now */
++		    if (*t == '\0') {
++			return 0;
++		    }
++		    t++;
++		}
++		/* coalesce into a single wildcard */
++		p++;
++	    }
++	    if (*p == '\0') {
++		/* wildcard at end of string, any remaining text is ok */
++		return 1;
++	    }
++
++	    while (*t != '\0') {
++		/* recurse */
++		if (octet_matches_(t, p, casemap)) return 1;
++		t++;
++	    }
++	case '\\':
++	    p++;
++	    /* falls through */
++	default:
++	    if (casemap && (toupper(c) == toupper(*t))) {
++		t++;
++	    } else if (!casemap && (c == *t)) {
++		t++;
++	    } else {
++		/* literal char doesn't match */
++		return 0;
++	    }
++	}
++    }
++    /* never reaches */
++    abort();
++}
++
++static int octet_matches(const char *text, const char *pat, 
++                         void *rock __attr_unused__)
++{
++    return octet_matches_(text, pat, 0);
++}
++
++
++#ifdef ENABLE_REGEX
++static int octet_regex(const char *text, const char *pat, 
++                       void *rock __attr_unused__)
++{
++    return (!regexec((regex_t *) pat, text, 0, NULL, 0));
++}
++#endif
++
++
++/* --- i;ascii-casemap comparators --- */
++
++
++/* use strcasecmp() as the compare function */
++
++/* sheer brute force */
++static int ascii_casemap_contains(const char *text, const char *pat,
++				  void *rock __attr_unused__)
++{
++    int N = strlen(text);
++    int M = strlen(pat);
++    int i, j;
++
++    i = 0, j = 0;
++    while ((j < M) && (i < N)) {
++              if (toupper(text[i]) == toupper(pat[j])) {
++	  	  i++; j++;
++	} else {
++	    i = i - j + 1;
++	    j = 0;
++	}
++    }    
++
++    return (j == M); /* we found a match! */
++}
++
++static int ascii_casemap_matches(const char *text, const char *pat, 
++                                 void *rock __attr_unused__)
++{
++    return octet_matches_(text, pat, 1);
++}
++
++/* i;ascii-numeric; only supports relational tests
++ *
++ *  A \ B    number   not-num 
++ *  number   A ? B    B > A 
++ *  not-num  A > B    A == B
++ */
++
++ /* From RFC 2244:
++  *
++  * The i;ascii-numeric comparator interprets strings as decimal
++  * positive integers represented as US-ASCII digits.  All values
++  * which do not begin with a US-ASCII digit are considered equal
++  * with an ordinal value higher than all non-NIL single-valued
++  * attributes.  Otherwise, all US-ASCII digits (octet values
++  * 0x30 to 0x39) are interpreted starting from the beginning of
++  * the string to the first non-digit or the end of the string.
++  */
++
++static int ascii_numeric_cmp(const char *text, const char *pat)
++{
++    unsigned text_digit_len;
++    unsigned pat_digit_len;
++
++    if (isdigit((int) *pat)) {
++	if (isdigit((int) *text)) {
++	    /* Count how many digits each string has */
++	    for (text_digit_len = 0;
++		 isdigit((int) text[text_digit_len]);
++		 text_digit_len++);
++	    for (pat_digit_len = 0;
++		 isdigit((int) pat[pat_digit_len]);
++		 pat_digit_len++);
++
++	    if (text_digit_len < pat_digit_len) {
++		/* Pad "text" with leading 0s */
++		while (pat_digit_len > text_digit_len) {
++		    /* "text" can only be less or equal to "pat" */
++		    if ('0' < *pat) {
++			return (-1); 
++		    }
++		    pat++;
++		    pat_digit_len--;
++		}
++	    } else if (text_digit_len > pat_digit_len) {
++		/* Pad "pad" with leading 0s */
++		while (text_digit_len > pat_digit_len) {
++		    /* "pad" can only be greater or equal to "text" */
++		    if (*text > '0') {
++			return 1;
++		    }
++		    text++;
++		    text_digit_len--;
++		}
++	    }
++
++	    /* CLAIM: If we here, we have two non-empty digital suffixes
++	       of equal length */
++	    while (text_digit_len > 0) {
++		if (*text < *pat) {
++			return -1;
++		} else if (*text > *pat) {
++			return 1;
++		}
++		/* Characters are equal, carry on */
++		text++;
++		pat++;
++		text_digit_len--;
++	    }
++
++	    return (0);
++	} else {
++	    return 1;
++	}
++    } else if (isdigit((int) *text)) {
++	return -1;
++    } else {
++	return 0; /* both not digits */
++    }
++}
++
++static comparator_t *lookup_rel(int relation)
++{
++    comparator_t *ret;
++
++    ret = NULL;
++    switch (relation)
++      {
++      case B_EQ:
++	ret = &rel_eq;
++	break;
++      case B_NE:
++	ret = &rel_ne; 
++	break;
++      case B_GT: 
++	ret = &rel_gt; 
++	break;
++      case B_GE:
++         ret = &rel_ge; 
++	 break;
++      case B_LT:
++	ret = &rel_lt; 
++	break;
++      case B_LE:
++	ret = &rel_le; 
++      }
++
++    return ret;
++}
++
++comparator_t *lookup_comp(int comp, int mode, int relation,
++			  void **comprock)
++{
++    comparator_t *ret;
++
++    ret = NULL;
++    *comprock = NULL;
++#if VERBOSE
++    printf("comp%d mode%d relat%d     \n", comp, mode, relation); 
++#endif
++    switch (comp)
++      {
++      case B_OCTET:    
++ 	switch (mode) {
++	  case B_IS:
++	    ret = &rel_eq;
++	    *comprock = (void **) &octet_cmp;
++	    break;
++	  case B_CONTAINS:
++	    ret = &octet_contains;
++	    break;
++	  case B_MATCHES:
++	    ret = &octet_matches;
++	    break;
++#ifdef ENABLE_REGEX
++	  case B_REGEX:
++	    ret = &octet_regex;
++	    break;
++#endif
++	  case B_VALUE:
++	    ret = lookup_rel(relation);
++	    *comprock = (void **) &octet_cmp;
++	    break;
++	}
++	break; /*end of octet */
++      case B_ASCIICASEMAP:
++     	switch (mode) {
++	case B_IS:
++	    ret = &rel_eq;
++	    *comprock = (void **) &strcasecmp;
++	    break;
++	case B_CONTAINS:
++	    ret = &ascii_casemap_contains;
++	    break;
++	case B_MATCHES:
++	    ret = &ascii_casemap_matches;
++	    break;
++#ifdef ENABLE_REGEX
++	case B_REGEX:
++	    /* the ascii-casemap destinction is made during
++	       the compilation of the regex in verify_regex() */
++	    ret = &octet_regex;
++	    break;
++#endif
++	case B_VALUE:
++	    ret = lookup_rel(relation);
++	    *comprock = &strcasecmp;
++	    break;
++	}
++	break;/*end of ascii casemap */
++      case B_ASCIINUMERIC:
++	switch (mode) {
++	case B_IS:
++	    ret = &rel_eq;
++	    *comprock = (void **) &ascii_numeric_cmp;
++	    break;
++	case B_COUNT:
++	case B_VALUE:
++	    ret = lookup_rel(relation);
++	    *comprock = (void **) &ascii_numeric_cmp;
++	    break;
++	}
++	break;
++      }
++    return ret;
++}
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/comparator.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/comparator.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,48 @@
++/* comparator.h
++ * Larry Greenfield
++ * $Id$
++ */
++/***********************************************************
++        Copyright 1999 by Carnegie Mellon University
++
++                      All Rights Reserved
++
++Permission to use, copy, modify, and distribute this software and its
++documentation for any purpose and without fee is hereby granted,
++provided that the above copyright notice appear in all copies and that
++both that copyright notice and this permission notice appear in
++supporting documentation, and that the name of Carnegie Mellon
++University not be used in advertising or publicity pertaining to
++distribution of the software without specific, written prior
++permission.
++
++CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
++FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
++ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
++OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++******************************************************************/
++
++#ifndef COMPARATOR_H
++#define COMPARATOR_H
++
++#ifdef ENABLE_REGEX
++#ifdef HAVE_RX
++#include <rxposix.h>
++#else
++#include <sys/types.h>
++#include <regex.h>
++#endif
++#endif
++
++/* compares pat to text; returns 1 if it's true, 0 otherwise 
++   first arg is text, second arg is pat, third arg is rock */
++typedef int comparator_t(const char *, const char *, void *);
++
++/* returns a pointer to a comparator function given it's name */
++comparator_t *lookup_comp(int comp, int mode,
++			  int relation, void **rock);
++
++#endif
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/interp.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/interp.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,203 @@
++/* interp.c -- sieve script interpretor builder
++ * Larry Greenfield
++ * $Id$
++ */
++/***********************************************************
++        Copyright 1999 by Carnegie Mellon University
++
++                      All Rights Reserved
++
++Permission to use, copy, modify, and distribute this software and its
++documentation for any purpose and without fee is hereby granted,
++provided that the above copyright notice appear in all copies and that
++both that copyright notice and this permission notice appear in
++supporting documentation, and that the name of Carnegie Mellon
++University not be used in advertising or publicity pertaining to
++distribution of the software without specific, written prior
++permission.
++
++CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
++FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
++ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
++OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++******************************************************************/
++
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include <stdlib.h>
++
++#include "xmalloc.h"
++
++#include "sieve_interface.h"
++#include "interp.h"
++
++/* build a sieve interpretor */
++int sieve_interp_alloc(sieve_interp_t **interp, void *interp_context)
++{
++    sieve_interp_t *i;
++    static int initonce;
++
++    if (!initonce) {
++	initialize_siev_error_table();
++	initonce = 1;
++    }
++
++    *interp = NULL;
++    i = (sieve_interp_t *) xmalloc(sizeof(sieve_interp_t));
++    if (i == NULL) {
++	return SIEVE_NOMEM;
++    }
++
++    i->redirect = i->discard = i->reject = i->fileinto = i->keep = NULL;
++    i->getsize = NULL;
++    i->getheader = NULL;
++    i->getenvelope = NULL;
++    i->vacation = NULL;
++    i->notify = NULL;
++
++    i->markflags = NULL;
++
++    i->interp_context = interp_context;
++    i->err = NULL;
++
++    *interp = i;
++    return SIEVE_OK;
++}
++
++static const char *sieve_extensions = "fileinto reject envelope vacation"
++                                      " imapflags notify subaddress relational"
++                                      " comparator-i;ascii-numeric"
++#ifdef ENABLE_REGEX
++" regex";
++#else
++"";
++#endif /* ENABLE_REGEX */
++
++const char *sieve_listextensions(void)
++{
++    return sieve_extensions;
++}
++
++int sieve_interp_free(sieve_interp_t **interp)
++{
++    free(*interp);
++    
++    return SIEVE_OK;
++}
++
++/* add the callbacks */
++int sieve_register_redirect(sieve_interp_t *interp, sieve_callback *f)
++{
++    interp->redirect = f;
++
++    return SIEVE_OK;
++}
++
++int sieve_register_discard(sieve_interp_t *interp, sieve_callback *f)
++{
++    interp->discard = f;
++
++    return SIEVE_OK;
++}
++
++int sieve_register_reject(sieve_interp_t *interp, sieve_callback *f)
++{
++    interp->reject = f;
++
++    return SIEVE_OK;
++}
++
++int sieve_register_fileinto(sieve_interp_t *interp, sieve_callback *f)
++{
++    interp->fileinto = f;
++
++    return SIEVE_OK;
++}
++
++int sieve_register_keep(sieve_interp_t *interp, sieve_callback *f)
++{
++    interp->keep = f;
++ 
++    return SIEVE_OK;
++}
++
++static char *default_markflags[] = { "\\flagged" };
++static sieve_imapflags_t default_mark = { default_markflags, 1 };
++
++int sieve_register_imapflags(sieve_interp_t *interp, sieve_imapflags_t *mark)
++{
++    interp->markflags =
++	(mark && mark->flag && mark->nflags) ? mark : &default_mark;
++
++    return SIEVE_OK;
++}
++
++int sieve_register_notify(sieve_interp_t *interp, sieve_callback *f)
++{
++    interp->notify = f;
++ 
++    return SIEVE_OK;
++}
++
++/* add the callbacks for messages. again, undefined if used after
++   sieve_script_parse */
++int sieve_register_size(sieve_interp_t *interp, sieve_get_size *f)
++{
++    interp->getsize = f;
++    return SIEVE_OK;
++}
++
++int sieve_register_header(sieve_interp_t *interp, sieve_get_header *f)
++{
++    interp->getheader = f;
++    return SIEVE_OK;
++}
++
++int sieve_register_envelope(sieve_interp_t *interp, sieve_get_envelope *f)
++{
++    interp->getenvelope = f;
++    return SIEVE_OK;
++}
++
++int sieve_register_vacation(sieve_interp_t *interp, sieve_vacation_t *v)
++{
++    if (!interp->getenvelope) {
++	return SIEVE_NOT_FINALIZED; /* we need envelope for vacation! */
++    }
++
++    if (v->min_response == 0) v->min_response = 3;
++    if (v->max_response == 0) v->max_response = 90;
++    if (v->min_response < 0 || v->max_response < 7 || !v->autorespond
++	|| !v->send_response) {
++	return SIEVE_FAIL;
++    }
++
++    interp->vacation = v;
++    return SIEVE_OK;
++}
++
++int sieve_register_parse_error(sieve_interp_t *interp, sieve_parse_error *f)
++{
++    interp->err = f;
++    return SIEVE_OK;
++}
++
++int sieve_register_execute_error(sieve_interp_t *interp, sieve_execute_error *f)
++{
++    interp->execute_err = f;
++    return SIEVE_OK;
++}
++
++int interp_verify(sieve_interp_t *i)
++{
++    if (i->redirect && i->keep && i->getsize && i->getheader) {
++	return SIEVE_OK;
++    } else {
++	return SIEVE_NOT_FINALIZED;
++    }
++}
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/interp.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/interp.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,56 @@
++/* interp.h -- interpretor definition
++ * Larry Greenfield
++ * $Id$
++ */
++/***********************************************************
++        Copyright 1999 by Carnegie Mellon University
++
++                      All Rights Reserved
++
++Permission to use, copy, modify, and distribute this software and its
++documentation for any purpose and without fee is hereby granted,
++provided that the above copyright notice appear in all copies and that
++both that copyright notice and this permission notice appear in
++supporting documentation, and that the name of Carnegie Mellon
++University not be used in advertising or publicity pertaining to
++distribution of the software without specific, written prior
++permission.
++
++CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
++FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
++ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
++OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++*****************************************************************/
++
++#ifndef SIEVE_INTERP_H
++#define SIEVE_INTERP_H
++
++#include "sieve_interface.h"
++
++struct sieve_interp {
++    /* standard callbacks for actions */
++    sieve_callback *redirect, *discard, *reject, *fileinto, *keep;
++    sieve_callback *notify;
++    sieve_vacation_t *vacation;
++
++    sieve_get_size *getsize;
++    sieve_get_header *getheader;
++    sieve_get_envelope *getenvelope;
++
++    sieve_parse_error *err;
++
++    /* site-specific imapflags for mark/unmark */
++    sieve_imapflags_t *markflags;
++
++    sieve_execute_error *execute_err;
++
++    /* context to pass along */
++    void *interp_context;
++};
++
++int interp_verify(sieve_interp_t *interp);
++
++#endif
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/message.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/message.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,580 @@
++/* message.c -- message parsing functions
++ * Larry Greenfield
++ * $Id$
++ */
++/***********************************************************
++        Copyright 1999 by Carnegie Mellon University
++
++                      All Rights Reserved
++
++Permission to use, copy, modify, and distribute this software and its
++documentation for any purpose and without fee is hereby granted,
++provided that the above copyright notice appear in all copies and that
++both that copyright notice and this permission notice appear in
++supporting documentation, and that the name of Carnegie Mellon
++University not be used in advertising or publicity pertaining to
++distribution of the software without specific, written prior
++permission.
++
++CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
++FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
++ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
++OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++******************************************************************/
++
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include <stdlib.h>
++#include <unistd.h>
++#include <sys/mman.h>
++#include <sys/stat.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++#include <string.h>
++
++#include "sieve_interface.h"
++#include "interp.h"
++#include "message.h"
++#include "parseaddr.h"
++#include "xmalloc.h"
++
++/* reject message m with message msg
++ *
++ * incompatible with: fileinto, redirect
++ */
++int do_reject(action_list_t *a, const char *msg)
++{
++    action_list_t *b = NULL;
++
++    /* see if this conflicts with any previous actions taken on this message */
++    while (a != NULL) {
++	b = a;
++	if (a->a == ACTION_FILEINTO ||
++	    a->a == ACTION_KEEP ||
++	    a->a == ACTION_REDIRECT ||
++	    a->a == ACTION_REJECT ||
++	    a->a == ACTION_VACATION ||
++	    a->a == ACTION_SETFLAG ||
++	    a->a == ACTION_ADDFLAG ||
++	    a->a == ACTION_REMOVEFLAG ||
++	    a->a == ACTION_MARK ||
++	    a->a == ACTION_UNMARK
++	    )
++	    return SIEVE_RUN_ERROR;
++	a = a->next;
++    }
++
++    /* add to the action list */
++    a = (action_list_t *) xmalloc(sizeof(action_list_t));
++    if (a == NULL)
++	return SIEVE_NOMEM;
++    a->a = ACTION_REJECT;
++    a->u.rej.msg = msg;
++    b->next = a;
++    a->next =  NULL;
++    return 0;
++}
++
++/* fileinto message m into mailbox 
++ *
++ * incompatible with: reject
++ */
++int do_fileinto(action_list_t *a, const char *mbox,
++		sieve_imapflags_t *imapflags)
++{
++    action_list_t *b = NULL;
++
++    /* see if this conflicts with any previous actions taken on this message */
++    while (a != NULL) {
++	b = a;
++	if (a->a == ACTION_REJECT)
++	    return SIEVE_RUN_ERROR;
++	a = a->next;
++    }
++
++    /* add to the action list */
++    a = (action_list_t *) xmalloc(sizeof(action_list_t));
++    if (a == NULL)
++	return SIEVE_NOMEM;
++    a->a = ACTION_FILEINTO;
++    a->u.fil.mailbox = mbox;
++    a->u.fil.imapflags = imapflags;
++    b->next = a;
++    a->next = NULL;
++    return 0;
++}
++
++/* redirect message m to to addr
++ *
++ * incompatible with: reject
++ */
++int do_redirect(action_list_t *a, const char *addr)
++{
++    action_list_t *b = NULL;
++
++    /* xxx we should validate addr */
++
++    /* see if this conflicts with any previous actions taken on this message */
++    while (a != NULL) {
++	b = a;
++	if (a->a == ACTION_REJECT)
++	    return SIEVE_RUN_ERROR;
++	a = a->next;
++    }
++
++    /* add to the action list */
++    a = (action_list_t *) xmalloc(sizeof(action_list_t));
++    if (a == NULL)
++	return SIEVE_NOMEM;
++    a->a = ACTION_REDIRECT;
++    a->u.red.addr = addr;
++    a->next = NULL;
++    b->next = a;
++    return 0;
++}
++
++/* keep message
++ *
++ * incompatible with: reject
++ */
++int do_keep(action_list_t *a, sieve_imapflags_t *imapflags)
++{
++    action_list_t *b = NULL;
++
++    /* see if this conflicts with any previous actions taken on this message */
++    while (a != NULL) {
++	b = a;
++	if (a->a == ACTION_REJECT)
++	    return SIEVE_RUN_ERROR;
++	if (a->a == ACTION_KEEP) /* don't bother doing it twice */
++	    return 0;
++	a = a->next;
++    }
++
++    /* add to the action list */
++    a = (action_list_t *) xmalloc(sizeof(action_list_t));
++    if (a == NULL)
++	return SIEVE_NOMEM;
++    a->a = ACTION_KEEP;
++    a->u.keep.imapflags = imapflags;
++    a->next = NULL;
++    b->next = a;
++    return 0;
++}
++
++/* discard message m
++ *
++ * incompatible with: nothing---it doesn't cancel any actions
++ */
++int do_discard(action_list_t *a)
++{
++    action_list_t *b = NULL;
++
++    /* see if this conflicts with any previous actions taken on this message */
++    while (a != NULL) {
++	b = a;
++	if (a->a == ACTION_DISCARD) /* don't bother doing twice */
++	    return 0;
++	a = a->next;
++    }
++
++    /* add to the action list */
++    a = (action_list_t *) xmalloc(sizeof(action_list_t));
++    if (a == NULL)
++	return SIEVE_NOMEM;
++    a->a = ACTION_DISCARD;
++    a->next = NULL;
++    b->next = a;
++    return 0;
++}
++
++int do_vacation(action_list_t *a, char *addr, char *fromaddr,
++		char *subj, const char *msg, int days,
++		int mime)
++{
++    action_list_t *b = NULL;
++
++    /* see if this conflicts with any previous actions taken on this message */
++    while (a != NULL) {
++	b = a;
++	if (a->a == ACTION_REJECT ||
++	    a->a == ACTION_VACATION) /* vacation can't be used twice */
++	    return SIEVE_RUN_ERROR;
++	a = a->next;
++    }
++
++    /* add to the action list */
++    a = (action_list_t *) xmalloc(sizeof(action_list_t));
++    if (a == NULL)
++	return SIEVE_NOMEM;
++    a->a = ACTION_VACATION;
++    a->u.vac.send.addr = addr;
++    a->u.vac.send.fromaddr = fromaddr;
++    a->u.vac.send.subj = subj;	/* user specified subject */
++    a->u.vac.send.msg = msg;
++    a->u.vac.send.mime = mime;
++    a->u.vac.autoresp.days = days;
++    a->next = NULL;
++    b->next = a;
++    return 0;
++}
++
++/* setflag f on message m
++ *
++ * incompatible with: reject
++ */
++int do_setflag(action_list_t *a, const char *flag)
++{
++    action_list_t *b = NULL;
++ 
++    /* see if this conflicts with any previous actions taken on this message */
++    while (a != NULL) {
++	b = a;
++	if (a->a == ACTION_REJECT)
++	    return SIEVE_RUN_ERROR;
++	a = a->next;
++    }
++ 
++    /* add to the action list */
++    a = (action_list_t *) xmalloc(sizeof(action_list_t));
++    if (a == NULL)
++	return SIEVE_NOMEM;
++    a->a = ACTION_SETFLAG;
++    a->u.fla.flag = flag;
++    b->next = a;
++    a->next = NULL;
++    return 0;
++}
++
++/* addflag f on message m
++ *
++ * incompatible with: reject
++ */
++int do_addflag(action_list_t *a, const char *flag)
++{
++    action_list_t *b = NULL;
++ 
++    /* see if this conflicts with any previous actions taken on this message */
++    while (a != NULL) {
++	b = a;
++	if (a->a == ACTION_REJECT)
++	    return SIEVE_RUN_ERROR;
++	a = a->next;
++    }
++ 
++    /* add to the action list */
++    a = (action_list_t *) xmalloc(sizeof(action_list_t));
++    if (a == NULL)
++	return SIEVE_NOMEM;
++    a->a = ACTION_ADDFLAG;
++    a->u.fla.flag = flag;
++    b->next = a;
++    a->next = NULL;
++    return 0;
++}
++
++/* removeflag f on message m
++ *
++ * incompatible with: reject
++ */
++int do_removeflag(action_list_t *a, const char *flag)
++{
++    action_list_t *b = NULL;
++ 
++    /* see if this conflicts with any previous actions taken on this message */
++    while (a != NULL) {
++	b = a;
++	if (a->a == ACTION_REJECT)
++	    return SIEVE_RUN_ERROR;
++	a = a->next;
++    }
++ 
++    /* add to the action list */
++    a = (action_list_t *) xmalloc(sizeof(action_list_t));
++    if (a == NULL)
++	return SIEVE_NOMEM;
++    a->a = ACTION_REMOVEFLAG;
++    a->u.fla.flag = flag;
++    b->next = a;
++    a->next = NULL;
++    return 0;
++}
++
++
++/* mark message m
++ *
++ * incompatible with: reject
++ */
++int do_mark(action_list_t *a)
++{
++    action_list_t *b = NULL;
++ 
++    /* see if this conflicts with any previous actions taken on this message */
++    while (a != NULL) {
++	b = a;
++	if (a->a == ACTION_REJECT)
++	    return SIEVE_RUN_ERROR;
++	a = a->next;
++    }
++ 
++    /* add to the action list */
++    a = (action_list_t *) xmalloc(sizeof(action_list_t));
++    if (a == NULL)
++	return SIEVE_NOMEM;
++    a->a = ACTION_MARK;
++    b->next = a;
++    a->next = NULL;
++    return 0;
++}
++
++
++/* unmark message m
++ *
++ * incompatible with: reject
++ */
++int do_unmark(action_list_t *a)
++{
++
++    action_list_t *b = NULL;
++    /* see if this conflicts with any previous actions taken on this message */
++    while (a != NULL) {
++	b = a;
++	if (a->a == ACTION_REJECT)
++	    return SIEVE_RUN_ERROR;
++	a = a->next;
++    }
++ 
++    /* add to the action list */
++    a = (action_list_t *) xmalloc(sizeof(action_list_t));
++    if (a == NULL)
++	return SIEVE_NOMEM;
++    a->a = ACTION_UNMARK;
++    b->next = a;
++    a->next = NULL;
++    return 0;
++}
++
++/* notify
++ *
++ * incompatible with: none
++ */
++int do_notify(notify_list_t *a, const char *id,
++	      const char *method, const char **options,
++	      const char *priority, const char *message)
++{
++    notify_list_t *b = NULL;
++
++    /* find the end of the notify list */
++    while (a != NULL) {
++	b = a;
++	a = a->next;
++    }
++
++    /* add to the notify list */
++    a = (notify_list_t *) xmalloc(sizeof(notify_list_t));
++    if (a == NULL)
++	return SIEVE_NOMEM;
++
++    b->next = a;
++    a->isactive = 1;
++    a->id = id;
++    a->method = method;
++    a->options = options;
++    a->priority = priority;
++    a->message = message;
++    a->next = NULL;
++    return 0;
++}
++
++/* denotify
++ *
++ * incomaptible with: none
++ */
++int do_denotify(notify_list_t *n, comparator_t *comp, const void *pat,
++		void *comprock, const char *priority)
++{
++    while (n != NULL) {
++	if (n->isactive && 
++	    (!priority || !strcasecmp(n->priority, priority)) &&
++	    (!comp || (n->id && comp(n->id, pat, comprock)))) {
++	    n->isactive = 0;
++	}
++	n = n->next;
++    }
++
++    return 0;
++}
++
++
++
++/* given a header, extract an address out of it.  if marker points to NULL,
++   extract the first address.  otherwise, it's an index into the header to
++   say where to start extracting */
++struct addr_marker {
++    struct address *where;
++    char *freeme;
++};
++
++int parse_address(const char *header, void **data, void **marker)
++{
++    struct addr_marker *am = (struct addr_marker *) *marker;
++
++    parseaddr_list(header, (struct address **) data);
++    am = (void *) xmalloc(sizeof(struct addr_marker));
++    am->where = *data;
++    am->freeme = NULL;
++    *marker = am;
++    return SIEVE_OK;
++}
++
++char *get_address(address_part_t addrpart,
++		  void **data __attr_unused__,
++		  void **marker,
++		  int canon_domain)
++{
++    char *ret = NULL;
++    struct address *a;
++    struct addr_marker *am = *marker;
++
++    a = am->where;
++    if (am->freeme) {
++	free(am->freeme);
++	am->freeme = NULL;
++    }
++
++    if (a == NULL) {
++	ret = NULL;
++    } else {
++	if (canon_domain && a->domain)
++	    lcase(a->domain);
++
++	switch (addrpart) { 
++	case ADDRESS_ALL:
++#define U_DOMAIN "unspecified-domain"
++#define U_USER "unknown-user"
++	    if (a->mailbox || a->domain) {
++		char *m = a->mailbox ? a->mailbox : U_USER;
++		char *d = a->domain ? a->domain : U_DOMAIN;
++		am->freeme = (char *) xmalloc(strlen(m) + strlen(d) + 2);
++
++		sprintf(am->freeme, "%s@%s", m, d);
++		ret = am->freeme;
++	    } else {
++		ret = NULL;
++	    }
++	    break;
++
++	case ADDRESS_LOCALPART:
++	    ret = a->mailbox;
++	    break;
++	    
++	case ADDRESS_DOMAIN:
++	    ret = a->domain;
++	    break;
++
++	case ADDRESS_USER:
++	    if (a->mailbox) {
++		char *p = strchr(a->mailbox, '+');
++		int len = p ? p - a->mailbox : (int)strlen(a->mailbox);
++
++		am->freeme = (char *) xmalloc(len + 1);
++		strncpy(am->freeme, a->mailbox, len);
++		am->freeme[len] = '\0';
++		ret = am->freeme;
++	    } else {
++		ret = NULL;
++	    }
++	    break;
++
++	case ADDRESS_DETAIL:
++	    if (a->mailbox)
++	    {	    
++		char *p = strchr(a->mailbox, '+');
++		ret = (p ? p + 1 : NULL);
++	    }
++	    else
++	    {
++		ret = NULL;
++	    }
++	    break;
++	}
++	a = a->next;
++	am->where = a;
++    }
++    *marker = am;
++    return ret;
++}
++
++int free_address(void **data, void **marker)
++{
++    struct addr_marker *am = (struct addr_marker *) *marker;
++
++    if (*data)
++	parseaddr_free((struct address *) *data);
++    *data = NULL;
++    if (am->freeme) free(am->freeme);
++    free(am);
++    *marker = NULL;
++    return SIEVE_OK;
++}
++
++notify_list_t *new_notify_list(void)    
++{
++    notify_list_t *ret = xmalloc(sizeof(notify_list_t));
++
++    if (ret != NULL) {
++	ret->isactive = 0;
++	ret->id       = NULL;
++	ret->method   = NULL;
++	ret->options  = NULL;
++	ret->priority = NULL;
++	ret->message  = NULL;
++	ret->next     = NULL;
++    }
++    return ret;
++}
++
++void free_notify_list(notify_list_t *n)
++{
++    while (n) {
++	notify_list_t *b = n->next;
++	free(n->options); /* strings live in bytecode, only free the array */
++	free(n);
++	n = b;
++    }
++}
++
++action_list_t *new_action_list(void)
++{
++    action_list_t *ret = xmalloc(sizeof(action_list_t));
++
++    if (ret != NULL) {
++	ret->a = ACTION_NONE;
++	ret->param = NULL;
++	ret->next = NULL;
++    }
++    return ret;
++}
++
++void free_action_list(action_list_t *a)
++{
++    while (a) {
++	action_list_t *b = a->next;
++
++	if(a->a == ACTION_VACATION) {
++	    if(a->u.vac.send.subj) free(a->u.vac.send.subj);
++	    if(a->u.vac.send.addr) free(a->u.vac.send.addr);
++	    if(a->u.vac.send.fromaddr) free(a->u.vac.send.fromaddr);
++	}
++
++	free(a);
++	a = b;
++    }
++}
++
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/message.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/message.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,140 @@
++/* message.h
++ * Larry Greenfield
++ * $Id$
++ */
++/***********************************************************
++        Copyright 1999 by Carnegie Mellon University
++
++                      All Rights Reserved
++
++Permission to use, copy, modify, and distribute this software and its
++documentation for any purpose and without fee is hereby granted,
++provided that the above copyright notice appear in all copies and that
++both that copyright notice and this permission notice appear in
++supporting documentation, and that the name of Carnegie Mellon
++University not be used in advertising or publicity pertaining to
++distribution of the software without specific, written prior
++permission.
++
++CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
++FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
++ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
++OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++******************************************************************/
++
++#ifndef MESSAGE_H
++#define MESSAGE_H
++
++#include "sieve_interface.h"	/* for action contexts */
++#include "tree.h"		/* for stringlist_t */
++
++typedef struct Action action_list_t;
++
++typedef enum {
++    ACTION_NULL = -1,
++    ACTION_NONE = 0,
++    ACTION_REJECT,
++    ACTION_FILEINTO,
++    ACTION_KEEP,
++    ACTION_REDIRECT,
++    ACTION_DISCARD,
++    ACTION_VACATION,
++    ACTION_SETFLAG,
++    ACTION_ADDFLAG,
++    ACTION_REMOVEFLAG,
++    ACTION_MARK,
++    ACTION_UNMARK,
++    ACTION_NOTIFY,
++    ACTION_DENOTIFY
++} action_t;
++
++/* information */
++action_list_t *new_action_list(void);
++void free_action_list(action_list_t *actions);
++
++/* invariant: always have a dummy element when free_action_list, param
++   and vac_subj are freed.  none of the others are automatically freed.
++
++   the do_action() functions should copy param */
++struct Action {
++    action_t a;
++    union {
++	sieve_reject_context_t rej;
++	sieve_fileinto_context_t fil;
++	sieve_keep_context_t keep;
++	sieve_redirect_context_t red;
++	struct {
++	    /* addr, fromaddr, subj - freed! */
++	    sieve_send_response_context_t send;
++	    sieve_autorespond_context_t autoresp;
++	} vac;
++	struct {
++	    const char *flag;
++	} fla;
++    } u;
++    char *param;		/* freed! */
++    struct Action *next;
++    char *vac_subj;		/* freed! */
++    char *vac_msg;
++    int vac_days;
++};
++
++typedef struct notify_list_s {
++    int isactive;
++    const char *id;
++    const char *method;
++    const char **options;
++    const char *priority;
++    const char *message;
++    struct notify_list_s *next;
++} notify_list_t;
++
++/* header parsing */
++typedef enum {
++    ADDRESS_ALL,
++    ADDRESS_LOCALPART,
++    ADDRESS_DOMAIN,
++    ADDRESS_USER,
++    ADDRESS_DETAIL
++} address_part_t;
++
++int parse_address(const char *header, void **data, void **marker);
++char *get_address(address_part_t addrpart, void **data, void **marker,
++		  int canon_domain);
++int free_address(void **data, void **marker);
++notify_list_t *new_notify_list(void);
++void free_notify_list(notify_list_t *n);
++
++/* actions; return negative on failure.
++ * these don't actually perform the actions, they just add it to the
++ * action list */
++int do_reject(action_list_t *m, const char *msg);
++int do_fileinto(action_list_t *m, const char *mbox,
++		sieve_imapflags_t *imapflags);
++int do_redirect(action_list_t *m, const char *addr);
++int do_keep(action_list_t *m, sieve_imapflags_t *imapflags);
++int do_discard(action_list_t *m);
++int do_vacation(action_list_t *m, char *addr, char *fromaddr,
++		char *subj, const char *msg, int days, int mime);
++int do_setflag(action_list_t *m, const char *flag);
++int do_addflag(action_list_t *m, const char *flag);
++int do_removeflag(action_list_t *m, const char *flag);
++int do_mark(action_list_t *m);
++int do_unmark(action_list_t *m);
++int do_notify(notify_list_t *n, const char *id,
++	      const char *method, const char **options,
++	      const char *priority, const char *message);
++int do_denotify(notify_list_t *n, comparator_t *comp, const void *pat,
++		void *comprock, const char *priority);
++
++/* execute some bytecode */
++int sieve_eval_bc(sieve_interp_t *i, const void *bc_in, unsigned int bc_len,
++		  void *m, sieve_imapflags_t * imapflags,
++		  action_list_t *actions,
++		  notify_list_t *notify_list,
++		  const char **errmsg);
++
++#endif
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/parseaddr.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/parseaddr.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,370 @@
++/* parseaddr.c -- RFC 822 address parser
++ * $Id$
++ *
++ * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer. 
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in
++ *    the documentation and/or other materials provided with the
++ *    distribution.
++ *
++ * 3. The name "Carnegie Mellon University" must not be used to
++ *    endorse or promote products derived from this software without
++ *    prior written permission. For permission or any other legal
++ *    details, please contact  
++ *      Office of Technology Transfer
++ *      Carnegie Mellon University
++ *      5000 Forbes Avenue
++ *      Pittsburgh, PA  15213-3890
++ *      (412) 268-4387, fax: (412) 268-7395
++ *      tech-transfer@andrew.cmu.edu
++ *
++ * 4. Redistributions of any form whatsoever must retain the following
++ *    acknowledgment:
++ *    "This product includes software developed by Computing Services
++ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
++ *
++ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
++ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
++ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
++ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
++ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ *
++ */
++
++#include <config.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <ctype.h>
++#include <string.h>
++
++#include "parseaddr.h"
++
++#define xmalloc malloc
++#define xstrdup strdup
++
++static char parseaddr_unspecified_domain[] = "unspecified-domain";
++
++static void parseaddr_append (struct address ***addrpp, char *name,
++				char *route, char *mailbox, char *domain,
++				char **freemep);
++static int parseaddr_phrase (char **inp, char **phrasep, char *specials);
++static int parseaddr_domain (char **inp, char **domainp, char **commmentp);
++static int parseaddr_route (char **inp, char **routep);
++
++/*
++ * Parse an address list in 's', appending address structures to
++ * the list pointed to by 'addrp'.
++ */
++void
++parseaddr_list(str, addrp)
++const char *str;
++struct address **addrp;
++{
++    char *s;
++    int ingroup = 0;
++    char *freeme;
++    int tok = ' ';
++    char *phrase, *route, *mailbox, *domain, *comment;
++
++    /* Skip down to the tail */
++    while (*addrp) {
++	addrp = &(*addrp)->next;
++    }
++
++    s = freeme = xstrdup(str);
++
++    while (tok) {
++	tok = parseaddr_phrase(&s, &phrase, ingroup ? ",@<;" : ",@<:");
++	switch (tok) {
++	case ',':
++	case '\0':
++	case ';':
++	    if (*phrase) {
++		parseaddr_append(&addrp, 0, 0, phrase, "", &freeme);
++	    }
++	    if (tok == ';') {
++		parseaddr_append(&addrp, 0, 0, 0, 0, &freeme);
++		ingroup = 0;
++	    }
++	    continue;
++
++	case ':':
++	    parseaddr_append(&addrp, 0, 0, phrase, 0, &freeme);
++	    ingroup++;
++	    continue;
++
++	case '@':
++	    tok = parseaddr_domain(&s, &domain, &comment);
++	    parseaddr_append(&addrp, comment, 0, phrase, domain, &freeme);
++	    continue;
++
++	case '<':
++	    tok = parseaddr_phrase(&s, &mailbox, "@>");
++	    if (tok == '@') {
++		route = 0;
++		if (!*mailbox) {
++		    *--s = '@';
++		    tok = parseaddr_route(&s, &route);
++		    if (tok != ':') {
++			parseaddr_append(&addrp, phrase, route, "", "", &freeme);
++			while (tok && tok != '>') tok = *s++;
++			continue;
++		    }
++		    tok = parseaddr_phrase(&s, &mailbox, "@>");
++		    if (tok != '@') {
++			parseaddr_append(&addrp, phrase, route, mailbox, "",
++					 &freeme);
++			continue;
++		    }
++		}
++		tok = parseaddr_domain(&s, &domain, 0);
++		parseaddr_append(&addrp, phrase, route, mailbox, domain,
++				 &freeme);
++		while (tok && tok != '>') tok = *s++;
++		continue; /* effectively auto-inserts a comma */
++	    }
++	    else {
++		parseaddr_append(&addrp, phrase, 0, mailbox, "", &freeme);
++	    }
++	}
++    }
++    if (ingroup) parseaddr_append(&addrp, 0, 0, 0, 0, &freeme);
++
++    if (freeme) free(freeme);
++}
++
++/*
++ * Free the address list 'addr'
++ */
++void
++parseaddr_free(addr)
++struct address *addr;
++{
++    struct address *next;
++
++    while (addr) {
++	if (addr->freeme) free(addr->freeme);
++	next = addr->next;
++	free((char *)addr);
++	addr = next;
++    }
++}
++
++/*
++ * Helper function to append a new address structure to and address list.
++ */
++static void
++parseaddr_append(addrpp, name, route, mailbox, domain, freemep)
++struct address ***addrpp;
++char *name;
++char *route;
++char *mailbox;
++char *domain;
++char **freemep;
++{
++    struct address *newaddr;
++
++    newaddr = (struct address *)xmalloc(sizeof(struct address));
++    if (name && *name) {
++	newaddr->name = name;
++    }
++    else {
++	newaddr->name = 0;
++    }
++
++    if (route && *route) {
++	newaddr->route = route;
++    }
++    else {
++	newaddr->route = 0;
++    }
++
++    newaddr->mailbox = mailbox;
++
++    if (domain && !*domain) {
++	domain = parseaddr_unspecified_domain;
++    }
++    newaddr->domain = domain;
++
++    newaddr->next = 0;
++    newaddr->freeme = *freemep;
++    *freemep = 0;
++
++    **addrpp = newaddr;
++    *addrpp = &newaddr->next;
++}
++
++/* Macro to skip white space and rfc822 comments */
++
++#define SKIPWHITESPACE(s) \
++{ \
++    int _c, _comment = 0; \
++ \
++    while ((_c = *(s))) { \
++	if (_c == '(') { \
++	    _comment = 1; \
++	    (s)++; \
++	    while ((_comment && (_c = *(s)))) { \
++		(s)++; \
++		if (_c == '\\' && *(s)) (s)++; \
++		else if (_c == '(') _comment++; \
++		else if (_c == ')') _comment--; \
++	    } \
++	    (s)--; \
++	} \
++	else if (!isspace(_c)) break; \
++	(s)++; \
++    } \
++}
++
++/*
++ * Parse an RFC 822 "phrase", stopping at 'specials'
++ */
++static int parseaddr_phrase(inp, phrasep, specials)
++char **inp;
++char **phrasep;
++char *specials;
++{
++    int c;
++    char *src = *inp;
++    char *dst;
++
++    SKIPWHITESPACE(src);
++
++    *phrasep = dst = src;
++
++    for (;;) {
++        c = *src++;
++	if (c == '\"') {
++	    while ((c = *src)) {
++		src++;
++		if (c == '\"') break;
++		if (c == '\\') {
++		    if (!(c = *src)) break;
++		    src++;
++		}
++		*dst++ = c;
++	    }
++	}
++	else if (isspace(c) || c == '(') {
++	    src--;
++	    SKIPWHITESPACE(src);
++	    *dst++ = ' ';
++	}
++	else if (!c || strchr(specials, c)) {
++	    if (dst > *phrasep && dst[-1] == ' ') dst--;
++	    *dst = '\0';
++	    *inp = src;
++	    return c;
++	}
++	else {
++	    *dst++ = c;
++	}
++    }
++}
++
++/*
++ * Parse a domain.  If 'commentp' is non-nil, parses any trailing comment
++ */
++static int parseaddr_domain(inp, domainp, commentp)
++char **inp;
++char **domainp;
++char **commentp;
++{
++    int c;
++    char *src = *inp;
++    char *dst;
++    char *cdst;
++    int comment;
++
++    if (commentp) *commentp = 0;
++    SKIPWHITESPACE(src);
++
++    *domainp = dst = src;
++
++    for (;;) {
++        c = *src++;
++	if (isalnum(c) || c == '-' || c == '[' || c == ']' || c == ':') {
++	    *dst++ = c;
++	    if (commentp) *commentp = 0;
++	}
++	else if (c == '.') {
++	    if (dst > *domainp && dst[-1] != '.') *dst++ = c;
++	    if (commentp) *commentp = 0;
++	}
++	else if (c == '(') {
++	    if (commentp) {
++		*commentp = cdst = src;
++		comment = 1;
++		while (comment && (c = *src)) {
++		    src++;
++		    if (c == '(') comment++;
++		    else if (c == ')') comment--;
++		    else if (c == '\\' && (c = *src)) src++;
++
++		    if (comment) *cdst++ = c;
++		}
++		*cdst = '\0';
++	    }
++	    else {
++		src--;
++		SKIPWHITESPACE(src);
++	    }
++	}
++	else if (!isspace(c)) {
++	    if (dst > *domainp && dst[-1] == '.') dst--;
++	    *dst = '\0';
++	    *inp = src;
++	    return c;
++	}
++    }
++}
++	
++/*
++ * Parse a source route (at-domain-list)
++ */
++static int parseaddr_route(inp, routep)
++char **inp;
++char **routep;
++{
++    int c;
++    char *src = *inp;
++    char *dst;
++
++    SKIPWHITESPACE(src);
++
++    *routep = dst = src;
++
++    for (;;) {
++        c = *src++;
++	if (isalnum(c) || c == '-' || c == '[' || c == ']' ||
++	    c == ',' || c == '@') {
++	    *dst++ = c;
++	}
++	else if (c == '.') {
++	    if (dst > *routep && dst[-1] != '.') *dst++ = c;
++	}
++	else if (isspace(c) || c == '(') {
++	    src--;
++	    SKIPWHITESPACE(src);
++	}
++	else {
++	    while (dst > *routep &&
++		   (dst[-1] == '.' || dst[-1] == ',' || dst[-1] == '@')) dst--;
++	    *dst = '\0';
++	    *inp = src;
++	    return c;
++	}
++    }
++}
++
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/parseaddr.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/parseaddr.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,69 @@
++/* parseaddr.h -- RFC 822 address parser
++ $Id$
++ 
++ * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer. 
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in
++ *    the documentation and/or other materials provided with the
++ *    distribution.
++ *
++ * 3. The name "Carnegie Mellon University" must not be used to
++ *    endorse or promote products derived from this software without
++ *    prior written permission. For permission or any other legal
++ *    details, please contact  
++ *      Office of Technology Transfer
++ *      Carnegie Mellon University
++ *      5000 Forbes Avenue
++ *      Pittsburgh, PA  15213-3890
++ *      (412) 268-4387, fax: (412) 268-7395
++ *      tech-transfer@andrew.cmu.edu
++ *
++ * 4. Redistributions of any form whatsoever must retain the following
++ *    acknowledgment:
++ *    "This product includes software developed by Computing Services
++ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
++ *
++ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
++ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
++ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
++ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
++ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ *
++ *
++ */
++
++#ifndef INCLUDED_PARSEADDR_H
++#define INCLUDED_PARSEADDR_H
++
++#ifndef P
++#ifdef __STDC__
++#define P(x) x
++#else
++#define P(x) ()
++#endif
++#endif
++
++struct address {
++    char *name;
++    char *route;
++    char *mailbox;
++    char *domain;
++    struct address *next;
++    char *freeme;		/* If non-nil, free */
++};
++
++extern void parseaddr_list P((const char *s, struct address **addrp));
++extern void parseaddr_free P((struct address *addr));
++
++
++#endif /* INCLUDED_PARSEADDR_H */
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/script.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/script.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,814 @@
++/* script.c -- sieve script functions
++ * Larry Greenfield
++ * $Id$
++ */
++/***********************************************************
++        Copyright 1999 by Carnegie Mellon University
++
++                      All Rights Reserved
++
++Permission to use, copy, modify, and distribute this software and its
++documentation for any purpose and without fee is hereby granted,
++provided that the above copyright notice appear in all copies and that
++both that copyright notice and this permission notice appear in
++supporting documentation, and that the name of Carnegie Mellon
++University not be used in advertising or publicity pertaining to
++distribution of the software without specific, written prior
++permission.
++
++CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
++FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
++ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
++OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++******************************************************************/
++
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include <stdlib.h>
++#include <string.h>
++#include <ctype.h>
++#include <sys/stat.h>
++#include <sys/types.h>
++#include <unistd.h>
++#include <assert.h>
++
++#include "xmalloc.h"
++
++#include "md5.h"
++#include "sieve_interface.h"
++#include "interp.h"
++#include "script.h"
++#include "tree.h"
++#include "map.h"
++#include "sieve.h"
++#include "message.h"
++#include "bytecode.h"
++
++/* does this interpretor support this requirement? */
++int script_require(sieve_script_t *s, char *req)
++{
++    if (!strcmp("fileinto", req)) {
++	if (s->interp.fileinto) {
++	    s->support.fileinto = 1;
++	    return 1;
++	} else {
++	    return 0;
++	}
++    } else if (!strcmp("reject", req)) {
++	if (s->interp.reject) {
++	    s->support.reject = 1;
++	    return 1;
++	} else {
++	    return 0;
++	}
++    } else if (!strcmp("envelope", req)) {
++	if (s->interp.getenvelope) {
++	    s->support.envelope = 1;
++	    return 1;
++	} else {
++	    return 0;
++	}
++    } else if (!strcmp("vacation", req)) {
++	if (s->interp.vacation) {
++	    s->support.vacation = 1;
++	    return 1;
++	} else {
++	    return 0;
++	}
++    } else if (!strcmp("imapflags", req)) {
++	if (s->interp.markflags->flag) {
++	    s->support.imapflags = 1;
++	    return 1;
++	} else {
++	    return 0;
++	}
++    } else if (!strcmp("notify",req)) {
++	if (s->interp.notify) {
++	    s->support.notify = 1;
++	    return 1;
++	} else {
++	    return 0;
++	}
++#ifdef ENABLE_REGEX
++    } else if (!strcmp("regex", req)) {
++	s->support.regex = 1;
++	return 1;
++#endif
++    } else if (!strcmp("subaddress", req)) {
++	s->support.subaddress = 1;
++	return 1;
++    } else if (!strcmp("relational", req)) {
++	s->support.relational = 1;
++	return 1;
++    } else if (!strcmp("comparator-i;octet", req)) {
++	return 1;
++    } else if (!strcmp("comparator-i;ascii-casemap", req)) {
++	return 1;
++    } else if (!strcmp("comparator-i;ascii-numeric", req)) {
++	s->support.i_ascii_numeric = 1;
++	return 1;
++    }
++    return 0;
++}
++
++/* given an interpretor and a script, produce an executable script */
++int sieve_script_parse(sieve_interp_t *interp, FILE *script,
++		       void *script_context, sieve_script_t **ret)
++{
++    sieve_script_t *s;
++    int res = SIEVE_OK;
++    extern int yylineno;
++
++    res = interp_verify(interp);
++    if (res != SIEVE_OK) {
++	return res;
++    }
++
++    s = (sieve_script_t *) xmalloc(sizeof(sieve_script_t));
++    s->interp = *interp;
++    s->script_context = script_context;
++    /* clear all support bits */
++    memset(&s->support, 0, sizeof(struct sieve_support));
++
++    s->err = 0;
++
++    yylineno = 1;		/* reset line number */
++    s->cmds = sieve_parse(s, script);
++    if (s->err > 0) {
++	if (s->cmds) {
++	    free_tree(s->cmds);
++	}
++	s->cmds = NULL;
++	res = SIEVE_PARSE_ERROR;
++    }
++
++    *ret = s;
++    return res;
++}
++
++static void free_imapflags(sieve_imapflags_t *imapflags)
++{
++    while (imapflags->nflags)
++	free(imapflags->flag[--imapflags->nflags]);
++    free(imapflags->flag);
++    
++    imapflags->flag = NULL;
++}
++  
++int sieve_script_free(sieve_script_t **s)
++{
++    if (*s) {
++	if ((*s)->cmds) {
++	    free_tree((*s)->cmds);
++	}
++	free(*s);
++    }
++
++    return SIEVE_OK;
++}
++ 
++#define GROW_AMOUNT 100
++
++static void add_header(sieve_interp_t *i, int isenv, char *header, 
++		       void *message_context, char **out, 
++		       int *outlen, int *outalloc)
++{
++    const char **h;
++    int addlen;
++    /* get header value */
++    if (isenv)
++	i->getenvelope(message_context, header, &h);	
++    else
++	i->getheader(message_context, header, &h);	
++
++    if (!h || !h[0])
++	return;
++
++    addlen = strlen(h[0]) + 1;
++
++    /* realloc if necessary */
++    if ( (*outlen) + addlen >= *outalloc)
++    {
++	*outalloc = (*outlen) + addlen + GROW_AMOUNT;
++	*out = xrealloc(*out, *outalloc);
++    }
++
++    /* add header value */
++    strcat(*out,h[0]);
++
++    *outlen += addlen;
++}
++
++static int fillin_headers(sieve_interp_t *i, const char *msg, 
++			  void *message_context, char **out, int *outlen)
++{
++    int allocsize = GROW_AMOUNT;
++    const char *c;
++    int n;
++
++    *out = xmalloc(GROW_AMOUNT);
++    *outlen = 0;
++    (*out)[0]='\0';
++
++    if (msg == NULL) return SIEVE_OK;
++
++    /* construct the message */
++    c = msg;
++    while (*c) {
++	/* expand variables */
++	if (!strncasecmp(c, "$from$", 6)) {
++	    add_header(i, 0 ,"From", message_context, out, outlen, &allocsize);
++	    c += 6;
++	}
++	else if (!strncasecmp(c, "$env-from$", 10)) {
++	    add_header(i, 1, "From", message_context, out, outlen, &allocsize);
++	    c += 10;
++	}
++	else if (!strncasecmp(c, "$subject$", 9)) {
++	    add_header(i, 0, "Subject", message_context, out, outlen, &allocsize);
++	    c += 9;
++	}
++	/* XXX need to do $text$ variables */
++	else {
++	    /* find length of plaintext up to next potential variable */
++	    n = strcspn(c+1, "$") + 1; /* skip opening '$' */
++	    /* realloc if necessary */
++	    if ( (*outlen) + n+1 >= allocsize) {
++		allocsize = (*outlen) + n+1 + GROW_AMOUNT;
++		*out = xrealloc(*out, allocsize);
++	    }
++	    /* copy the plaintext */
++	    strncat(*out, c, n);
++	    (*out)[*outlen+n]='\0';
++	    (*outlen) += n;
++	    c += n;
++	}
++    }
++
++    return SIEVE_OK;
++}
++
++static int sieve_addflag(sieve_imapflags_t *imapflags, const char *flag)
++{
++    int n;
++    /* search for flag already in list */
++    for (n = 0; n < imapflags->nflags; n++) {
++	if (!strcmp(imapflags->flag[n], flag))
++	    break;
++    }
++ 
++    /* add flag to list, iff not in list */
++    if (n == imapflags->nflags) {
++	imapflags->nflags++;
++	imapflags->flag =
++	    (char **) xrealloc((char *)imapflags->flag,
++			       imapflags->nflags*sizeof(char *));
++	imapflags->flag[imapflags->nflags-1] = xstrdup(flag);
++    }
++ 
++    return SIEVE_OK;
++}
++
++static int sieve_removeflag(sieve_imapflags_t *imapflags, const char *flag)
++{
++    int n;
++    /* search for flag already in list */
++    for (n = 0; n < imapflags->nflags; n++) {
++      if (!strcmp(imapflags->flag[n], flag))
++	break;
++    }
++    
++     /* remove flag from list, iff in list */
++    if (n < imapflags->nflags) 
++      {
++	free(imapflags->flag[n]);
++	imapflags->nflags--;
++	
++	for (; n < imapflags->nflags; n++)
++	  imapflags->flag[n] = imapflags->flag[n+1];
++	
++	if (imapflags->nflags)
++	  {imapflags->flag =
++	     (char **) xrealloc((char *)imapflags->flag,
++				imapflags->nflags*sizeof(char *));}
++	else
++	  {free(imapflags->flag);
++	  imapflags->flag=NULL;}
++      }
++    
++    return SIEVE_OK;
++}
++
++static int send_notify_callback(sieve_interp_t *interp, void *message_context, 
++				void * script_context, notify_list_t *notify, 
++				char *actions_string, const char **errmsg)
++{
++    sieve_notify_context_t nc;
++    char *out_msg, *build_msg;
++    int out_msglen;    
++    int ret;
++
++    assert(notify->isactive);
++
++    if (!notify->method || !notify->options ||
++	!notify->priority || !notify->message) {
++	return SIEVE_RUN_ERROR;
++    }
++
++    nc.method = notify->method;
++    nc.options = notify->options ? notify->options : NULL;
++    nc.priority = notify->priority;
++
++    fillin_headers(interp, notify->message, message_context, 
++		   &out_msg, &out_msglen);
++
++    build_msg = xmalloc(out_msglen + strlen(actions_string) + 30);
++
++    strcpy(build_msg, out_msg);
++    strcat(build_msg, "\n\n");
++    strcat(build_msg, actions_string);
++
++    nc.message = build_msg;
++
++    free(out_msg);
++
++    ret = interp->notify(&nc,
++			 interp->interp_context,
++			 script_context,
++			 message_context,
++			 errmsg);    
++
++    free(build_msg);
++
++    return ret;
++}
++
++static char *action_to_string(action_t action)
++{
++    switch(action)
++	{
++	case ACTION_REJECT: return "Reject";
++	case ACTION_FILEINTO: return "Fileinto";
++	case ACTION_KEEP: return "Keep";
++	case ACTION_REDIRECT: return "Redirect";
++	case ACTION_DISCARD: return "Discard";
++	case ACTION_VACATION: return "Vacation";
++	case ACTION_SETFLAG: return "Setflag";
++	case ACTION_ADDFLAG: return "Addflag";
++	case ACTION_REMOVEFLAG: return "Removeflag";
++	case ACTION_MARK: return "Mark";
++	case ACTION_UNMARK: return "Unmark";
++	case ACTION_NOTIFY: return "Notify";
++	case ACTION_DENOTIFY: return "Denotify";
++	default: return "Unknown";
++	}
++
++    return "Error!";
++}
++
++static char *sieve_errstr(int code)
++{
++    switch (code)
++	{
++	case SIEVE_FAIL: return "Generic Error";
++	case SIEVE_NOT_FINALIZED: return "Sieve not finalized";
++	case SIEVE_PARSE_ERROR: return "Parse error";
++	case SIEVE_RUN_ERROR: return "Run error";
++	case SIEVE_INTERNAL_ERROR: return "Internal Error";
++	case SIEVE_NOMEM: return "No memory";
++	default: return "Unknown error";
++	}
++
++    return "Error!";
++}
++
++#define HASHSIZE 16
++
++static int makehash(unsigned char hash[HASHSIZE],
++		    const char *s1, const char *s2)
++{
++    struct md5_context ctx;
++
++    md5_init(&ctx);
++    md5_update(&ctx, s1, strlen(s1));
++    md5_update(&ctx, s2, strlen(s2));
++    md5_final(&ctx, hash);
++
++    return SIEVE_OK;
++}
++
++
++/******************************bytecode functions*****************************
++ *****************************************************************************/
++
++/* Load a compiled script */
++int sieve_script_load(const char *fname, sieve_bytecode_t **ret) 
++{
++    struct stat sbuf;
++    sieve_bytecode_t *r;
++    int fd;
++   
++    if (!fname || !ret) return SIEVE_FAIL;
++    
++    fd = open(fname, O_RDONLY);
++    if (fd == -1) {
++	if (errno != ENOENT)
++	    i_error("IOERROR: can not open sieve script %s: %m", fname);
++	return SIEVE_FAIL;
++    }
++
++    if (fstat(fd, &sbuf) == -1) {
++	i_error("IOERROR: fstating sieve script %s: %m", fname);
++	close(fd);
++	return SIEVE_FAIL;
++    }
++
++    r = (sieve_bytecode_t *) xzmalloc(sizeof(sieve_bytecode_t));
++
++    r->fd = fd;
++    
++    map_refresh(fd, 1, &r->data, &r->len, sbuf.st_size, fname, "sievescript");
++
++    if ((r->len < (BYTECODE_MAGIC_LEN + 2*sizeof(bytecode_input_t))) ||
++	memcmp(r->data, BYTECODE_MAGIC, BYTECODE_MAGIC_LEN)) {
++	i_error("IOERROR: not a sieve bytecode file %s", fname);
++	sieve_script_unload(&r);
++	return SIEVE_FAIL;
++    }
++
++    *ret = r;
++    return SIEVE_OK;
++}
++
++
++
++int sieve_script_unload(sieve_bytecode_t **s) 
++{
++    if(s && *s) {
++	map_free(&((*s)->data), &((*s)->len));
++	close((*s)->fd);
++	free(*s);
++	*s = NULL;
++    } 
++    /*i added this else, i'm not sure why, but this function always returned SIEVE_FAIL*/
++    else
++      return SIEVE_FAIL;
++    return SIEVE_OK;
++}
++
++
++#define ACTIONS_STRING_LEN 4096
++
++static int do_sieve_error(int ret,
++			  sieve_interp_t *interp,
++			  void *script_context,
++			  void *message_context,
++			  sieve_imapflags_t * imapflags,
++			  action_list_t *actions,
++			  notify_list_t *notify_list,
++			  /* notify_action_t *notify_action,*/
++			  int lastaction,
++			  int implicit_keep,
++			  char *actions_string,
++			  const char *errmsg
++			  ) 
++{
++   if (ret != SIEVE_OK) {
++	if (lastaction == -1) /* we never executed an action */
++	    snprintf(actions_string+strlen(actions_string),
++		     ACTIONS_STRING_LEN-strlen(actions_string),
++		     "script execution failed: %s\n",
++		     errmsg ? errmsg : sieve_errstr(ret));
++	else
++	    snprintf(actions_string+strlen(actions_string),
++		     ACTIONS_STRING_LEN-strlen(actions_string),
++		     "%s action failed: %s\n",
++		     action_to_string(lastaction),
++		     errmsg ? errmsg : sieve_errstr(ret));
++    }
++ 
++   
++    /* Process notify actions */
++    if (interp->notify && notify_list) 
++      {
++	notify_list_t *n = notify_list;
++	int notify_ret = SIEVE_OK;
++	
++	while (n != NULL) 
++	  {
++	    if (n->isactive) 
++	      {
++	      lastaction = ACTION_NOTIFY;
++	       notify_ret = send_notify_callback(interp, message_context, 
++						script_context,n,
++						actions_string, &errmsg);
++	      ret |= notify_ret;
++	      }
++	    n = n->next;
++	  }
++	
++	if (notify_list) free_notify_list(notify_list);
++	notify_list = NULL;	/* don't try any notifications again */
++	
++	
++	if (notify_ret != SIEVE_OK) 
++	  return do_sieve_error(ret, interp, script_context, message_context,
++				imapflags, actions, notify_list, lastaction,
++				implicit_keep, actions_string, errmsg);
++      
++      }
++    
++    if ((ret != SIEVE_OK) && interp->err) {
++	char buf[1024];
++	if (lastaction == -1) /* we never executed an action */
++	    sprintf(buf, "%s", errmsg ? errmsg : sieve_errstr(ret));
++	else
++	    sprintf(buf, "%s: %s", action_to_string(lastaction),
++		    errmsg ? errmsg : sieve_errstr(ret));
++ 
++	ret |= interp->execute_err(buf, interp->interp_context,
++				   script_context, message_context);
++    }
++
++    if (implicit_keep) {
++	sieve_keep_context_t keep_context;
++	int keep_ret;
++	keep_context.imapflags = imapflags;
++ 
++	lastaction = ACTION_KEEP;
++	keep_ret = interp->keep(&keep_context, interp->interp_context,
++				script_context, message_context, &errmsg);
++	ret |= keep_ret;
++        if (keep_ret == SIEVE_OK)
++            snprintf(actions_string+strlen(actions_string),
++		     sizeof(actions_string)-strlen(actions_string),
++		     "Kept\n");
++	else {
++	    implicit_keep = 0;	/* don't try an implicit keep again */
++	    return do_sieve_error(ret, interp, script_context, message_context,
++				  imapflags, actions, notify_list, lastaction,
++				  implicit_keep, actions_string, errmsg);
++	}
++    }
++
++    if (actions)
++	free_action_list(actions);
++
++    return ret;
++}
++
++
++static int do_action_list(sieve_interp_t *interp,
++			  void *script_context,
++			  void *message_context,
++			  sieve_imapflags_t *imapflags,
++			  action_list_t *actions,
++			  notify_list_t *notify_list,
++			  /* notify_action_t *notify_action,*/
++			  char *actions_string,
++			  const char *errmsg) 
++{
++    action_list_t *a;
++    action_t lastaction = -1;
++    int ret = 0;
++    int implicit_keep = 0;
++    
++    strcpy(actions_string,"Action(s) taken:\n");
++  
++    /* now perform actions attached to m */
++    a = actions;
++    implicit_keep = 1;
++    while (a != NULL) {
++	lastaction = a->a;
++	errmsg = NULL;
++	switch (a->a) {
++	case ACTION_REJECT:
++	    implicit_keep = 0;
++	    if (!interp->reject)
++		return SIEVE_INTERNAL_ERROR;
++	    ret = interp->reject(&a->u.rej,
++				 interp->interp_context,
++				 script_context,
++				 message_context,
++				 &errmsg);
++	    
++	    if (ret == SIEVE_OK)
++		snprintf(actions_string+strlen(actions_string),
++			 sizeof(actions_string)-strlen(actions_string), 
++			 "Rejected with: %s\n", a->u.rej.msg);
++
++	    break;
++	case ACTION_FILEINTO:
++	    implicit_keep = 0;
++	    if (!interp->fileinto)
++		return SIEVE_INTERNAL_ERROR;
++	    ret = interp->fileinto(&a->u.fil,
++				   interp->interp_context,
++				   script_context,
++				   message_context,
++				   &errmsg);
++
++	    if (ret == SIEVE_OK)
++		snprintf(actions_string+strlen(actions_string),
++			 sizeof(actions_string)-strlen(actions_string),
++			 "Filed into: %s\n",a->u.fil.mailbox);
++	    break;
++	case ACTION_KEEP:
++	    implicit_keep = 0;
++	    if (!interp->keep)
++		return SIEVE_INTERNAL_ERROR;
++	    ret = interp->keep(&a->u.keep,
++			       interp->interp_context,
++			       script_context,
++			       message_context,
++			       &errmsg);
++	    if (ret == SIEVE_OK)
++		snprintf(actions_string+strlen(actions_string),
++			 sizeof(actions_string)-strlen(actions_string),
++			 "Kept\n");
++	    break;
++	case ACTION_REDIRECT:
++	    implicit_keep = 0;
++	    if (!interp->redirect)
++		return SIEVE_INTERNAL_ERROR;
++	    ret = interp->redirect(&a->u.red,
++				   interp->interp_context,
++				   script_context,
++				   message_context,
++				   &errmsg);
++	    if (ret == SIEVE_OK)
++		snprintf(actions_string+strlen(actions_string),
++			 sizeof(actions_string)-strlen(actions_string),
++			 "Redirected to %s\n", a->u.red.addr);
++	    break;
++	case ACTION_DISCARD:
++	    implicit_keep = 0;
++	    if (interp->discard) /* discard is optional */
++		ret = interp->discard(NULL, interp->interp_context,
++				      script_context,
++				      message_context,
++				      &errmsg);
++	    if (ret == SIEVE_OK)
++		snprintf(actions_string+strlen(actions_string),
++			 sizeof(actions_string)-strlen(actions_string),
++			 "Discarded\n");
++	    break;
++
++	case ACTION_VACATION:
++	    {
++		unsigned char hash[HASHSIZE];
++
++		if (!interp->vacation)
++		    return SIEVE_INTERNAL_ERROR;
++
++		/* first, let's figure out if we should respond to this */
++		ret = makehash(hash, a->u.vac.send.addr,
++			       a->u.vac.send.msg);
++
++		if (ret == SIEVE_OK) {
++		    a->u.vac.autoresp.hash = hash;
++		    a->u.vac.autoresp.len = HASHSIZE;
++		    ret = interp->vacation->autorespond(&a->u.vac.autoresp,
++							interp->interp_context,
++							script_context,
++							message_context,
++							&errmsg);
++		}
++		if (ret == SIEVE_OK) {
++		    /* send the response */
++		    ret = interp->vacation->send_response(&a->u.vac.send,
++							  interp->interp_context,
++							  script_context, 
++							  message_context,
++							  &errmsg);
++
++		    if (ret == SIEVE_OK)
++			snprintf(actions_string+strlen(actions_string),
++				 sizeof(actions_string)-strlen(actions_string),
++				 "Sent vacation reply\n");
++
++		} else if (ret == SIEVE_DONE) {
++		    snprintf(actions_string+strlen(actions_string),
++			     sizeof(actions_string)-strlen(actions_string),
++			     "Vacation reply suppressed\n");
++
++		    ret = SIEVE_OK;
++		}
++	    
++		break;
++	    }
++
++ 
++	case ACTION_SETFLAG:
++	    free_imapflags(imapflags);
++	    ret = sieve_addflag(imapflags, a->u.fla.flag);
++	    break;
++	case ACTION_ADDFLAG:
++	    ret = sieve_addflag(imapflags, a->u.fla.flag);
++	    break;
++	case ACTION_REMOVEFLAG:
++	    ret = sieve_removeflag(imapflags, a->u.fla.flag);
++	    break;
++	case ACTION_MARK:
++	    {
++		int n = interp->markflags->nflags;
++
++		ret = SIEVE_OK;
++		while (n && ret == SIEVE_OK) {
++		    ret = sieve_addflag(imapflags,
++					interp->markflags->flag[--n]);
++		}
++		break;
++	    }
++	case ACTION_UNMARK:
++	  {
++	   
++		int n = interp->markflags->nflags;
++		ret = SIEVE_OK;
++		while (n && ret == SIEVE_OK) {
++		    ret = sieve_removeflag(imapflags,
++					   interp->markflags->flag[--n]);
++		}
++		break;
++	    }
++
++	case ACTION_NONE:
++	    break;
++
++	default:
++	    ret = SIEVE_INTERNAL_ERROR;
++	    break;
++	}
++	a = a->next;
++
++	if (ret != SIEVE_OK) {
++	    /* uh oh! better bail! */
++	    break;
++	}
++    }
++
++    return do_sieve_error(ret, interp, script_context, message_context, 
++			  imapflags, actions, notify_list, lastaction, 
++			  implicit_keep, actions_string, errmsg);
++}
++
++
++int sieve_execute_bytecode(sieve_bytecode_t *bc, sieve_interp_t *interp,
++			   void *script_context, void *message_context) 
++{
++    action_list_t *actions = NULL;
++    notify_list_t *notify_list = NULL;
++    /*   notify_action_t *notify_action;*/
++    action_t lastaction = -1;
++    int ret;
++    char actions_string[ACTIONS_STRING_LEN] = "";
++    const char *errmsg = NULL;
++    sieve_imapflags_t imapflags;
++    
++    if (!interp) return SIEVE_FAIL;
++
++    imapflags.flag = NULL; 
++    imapflags.nflags = 0;
++    
++    if (interp->notify)
++    {
++	notify_list = new_notify_list();
++	if (notify_list == NULL)
++	    {
++		ret = SIEVE_NOMEM;
++		return do_sieve_error(ret, interp, script_context,
++				      message_context, &imapflags,
++				      actions, notify_list, lastaction, 0,
++				      actions_string, errmsg);
++	    }
++    }
++
++    actions = new_action_list();
++    if (actions == NULL) 
++    {
++	ret = SIEVE_NOMEM;
++	return do_sieve_error(ret, interp, script_context,
++			      message_context, &imapflags,
++			      actions, notify_list, lastaction, 0,
++			      actions_string, errmsg);
++    }
++    
++    if (sieve_eval_bc(interp, bc->data, bc->len, message_context, 
++		      &imapflags, actions, notify_list, &errmsg) < 0)
++    {
++	ret = SIEVE_RUN_ERROR;
++	return do_sieve_error(ret, interp, script_context,
++			      message_context, &imapflags,
++			      actions, notify_list, lastaction, 0,
++			      actions_string, errmsg);
++    }
++    
++    return do_action_list(interp, script_context, message_context, 
++			  &imapflags, actions, notify_list, actions_string,
++			  errmsg);
++}
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/script.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/script.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,74 @@
++/* script.h -- script definition
++ * Larry Greenfield
++ * $Id$
++ */
++/***********************************************************
++        Copyright 1999 by Carnegie Mellon University
++
++                      All Rights Reserved
++
++Permission to use, copy, modify, and distribute this software and its
++documentation for any purpose and without fee is hereby granted,
++provided that the above copyright notice appear in all copies and that
++both that copyright notice and this permission notice appear in
++supporting documentation, and that the name of Carnegie Mellon
++University not be used in advertising or publicity pertaining to
++distribution of the software without specific, written prior
++permission.
++
++CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
++FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
++ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
++OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++******************************************************************/
++
++#ifndef SIEVE_SCRIPT_H
++#define SIEVE_SCRIPT_H
++
++#include "sieve_interface.h"
++#include "interp.h"
++#include "tree.h"
++
++#define ADDRERR_SIZE 500
++
++struct sieve_script {
++    sieve_interp_t interp;
++
++    /* was a "require" done for these? */
++    struct sieve_support {
++	int fileinto       : 1;
++	int reject         : 1;
++	int envelope       : 1;
++	int vacation       : 1;
++	int imapflags      : 1;
++	int notify         : 1;
++	int regex          : 1;
++	int subaddress     : 1;
++	int relational     : 1;
++	int i_ascii_numeric: 1;
++    } support;
++
++    void *script_context;
++    commandlist_t *cmds;
++
++    int err;
++};
++
++struct sieve_bytecode
++{
++    sieve_interp_t *interp;
++    void *script_context;
++
++    const char *data;
++    unsigned long len;
++    int fd;
++};
++
++/* generated by the yacc script */
++commandlist_t *sieve_parse(sieve_script_t *script, FILE *f);
++int script_require(sieve_script_t *s, char *req);
++
++#endif
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/sieve-lex.l
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/sieve-lex.l	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,162 @@
++%{
++/* sieve.l -- sieve lexer
++ * Larry Greenfield
++ * $Id$
++ */
++/***********************************************************
++        Copyright 1999 by Carnegie Mellon University
++
++                      All Rights Reserved
++
++Permission to use, copy, modify, and distribute this software and its
++documentation for any purpose and without fee is hereby granted,
++provided that the above copyright notice appear in all copies and that
++both that copyright notice and this permission notice appear in
++supporting documentation, and that the name of Carnegie Mellon
++University not be used in advertising or publicity pertaining to
++distribution of the software without specific, written prior
++permission.
++
++CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
++FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
++ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
++OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++******************************************************************/
++
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include <string.h> /* for strdup */
++#include "xmalloc.h"
++
++#include "tree.h"
++#include "sieve.h"
++
++#define yylval sievelval
++#define yylex sievelex
++#define yyerror sieveerror
++
++static int tonum(char *c);
++static char *chkstr(char *);
++static char *mlbuf;
++static int mlbufsz, mlcur;
++extern int yyerror(char *);
++%}
++
++%option yylineno
++%option noyywrap
++%option nounput
++
++ws		[ \t]+
++ident		[a-zA-Z_][a-zA-Z_0-9]*
++CRLF		(\r\n|\r|\n)
++
++%state MULTILINE
++%state QSTRING
++
++%%
++<MULTILINE>^\.{CRLF}	{ BEGIN INITIAL; 
++                          if (mlbuf) mlbuf[mlcur] = '\0';
++                          yylval.sval = chkstr(mlbuf); return STRING; }
++<MULTILINE>^\.\.  { /* dot stuffing! we want one . */ yyless(1); }
++<MULTILINE>(.|\n) { if (mlcur == mlbufsz) 
++			mlbuf = xrealloc(mlbuf, 1 + (mlbufsz+=1024));
++		    mlbuf[mlcur++] = yytext[0]; }
++<MULTILINE><<EOF>> { yyerror("unexpected end of file in string"); 
++		     yyterminate(); }
++<QSTRING>\"        { BEGIN INITIAL;
++                     if (mlbuf) mlbuf[mlcur] = '\0';
++		     yylval.sval = chkstr(mlbuf); return STRING; }
++<QSTRING>\\.      { if (mlcur == mlbufsz) 
++			mlbuf = xrealloc(mlbuf, 1 + (mlbufsz+=1024));
++		    mlbuf[mlcur++] = yytext[1]; }
++<QSTRING>(.|\n)   { if (mlcur == mlbufsz) 
++			mlbuf = xrealloc(mlbuf, 1 + (mlbufsz+=1024));
++		    mlbuf[mlcur++] = yytext[0]; }
++<INITIAL>text:{ws}?(#.*)?{CRLF}	{ BEGIN MULTILINE;
++			  mlcur = 0; mlbufsz = 0; mlbuf = NULL; }
++<INITIAL>\"        { BEGIN QSTRING;
++                    mlcur = 0; mlbufsz = 0; mlbuf = NULL; }
++<INITIAL>[0-9]+[KMG]?	{ yylval.nval = tonum(yytext); return NUMBER; }
++<INITIAL>if		return IF;
++<INITIAL>elsif		return ELSIF;
++<INITIAL>else		return ELSE;
++<INITIAL>anyof		return ANYOF;
++<INITIAL>allof		return ALLOF;
++<INITIAL>exists		return EXISTS;
++<INITIAL>false		return SFALSE;
++<INITIAL>true		return STRUE;
++<INITIAL>address	return ADDRESS;
++<INITIAL>envelope	return ENVELOPE;
++<INITIAL>header		return HEADER;
++<INITIAL>not		return NOT;
++<INITIAL>size		return SIZE;
++<INITIAL>reject		return REJCT;
++<INITIAL>fileinto	return FILEINTO;
++<INITIAL>redirect	return REDIRECT;
++<INITIAL>keep		return KEEP;
++<INITIAL>require	return REQUIRE;
++<INITIAL>stop		return STOP;
++<INITIAL>discard	return DISCARD;
++<INITIAL>setflag	return SETFLAG;
++<INITIAL>addflag	return ADDFLAG;
++<INITIAL>removeflag	return REMOVEFLAG;
++<INITIAL>mark		return MARK;
++<INITIAL>unmark		return UNMARK;
++<INITIAL>notify		return NOTIFY;
++<INITIAL>denotify	return DENOTIFY;
++<INITIAL>:id		return ID;
++<INITIAL>:method	return METHOD;
++<INITIAL>:options	return OPTIONS;
++<INITIAL>:low		return LOW;
++<INITIAL>:normal	return NORMAL;
++<INITIAL>:high		return HIGH;
++<INITIAL>:message	return MESSAGE;
++<INITIAL>vacation	return VACATION;
++<INITIAL>:days		return DAYS;
++<INITIAL>:addresses	return ADDRESSES;
++<INITIAL>:subject	return SUBJECT;
++<INITIAL>:mime		return MIME;
++<INITIAL>:comparator	return COMPARATOR;
++<INITIAL>:is		return IS;
++<INITIAL>:contains	return CONTAINS;
++<INITIAL>:matches	return MATCHES;
++<INITIAL>:regex		return REGEX;
++<INITIAL>:count		return COUNT;
++<INITIAL>:value		return VALUE;
++<INITIAL>:over		return OVER;
++<INITIAL>:under		return UNDER;
++<INITIAL>:all		return ALL;
++<INITIAL>:localpart	return LOCALPART;
++<INITIAL>:domain	return DOMAIN;
++<INITIAL>:user		return USER;
++<INITIAL>:detail	return DETAIL;
++<INITIAL>[ \t\n\r] ;	/* ignore whitespace */
++<INITIAL>#.* ;		/* ignore hash comments */
++<INITIAL>"/*"([^\*]|\*[^\/])*\*?"*/" ;	/* ignore bracket comments */
++.			return yytext[0];
++
++%%
++/*  */
++static int tonum(char *c)
++{
++  int val = atoi(c);
++  switch (c[strlen(c)-1]) {
++  case 'K': val *= (1 << 10); break;
++  case 'M': val *= (1 << 20); break;
++  case 'G': val *= (1 << 30); break;
++  default: break;
++  }
++  return val;
++}
++
++/* convert NULL strings to "" */
++static char *chkstr(char *str)
++{
++    if (!str) return xstrdup("");
++    else return str;
++}
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/sieve.y
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/sieve.y	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,1090 @@
++%{
++/* sieve.y -- sieve parser
++ * Larry Greenfield
++ * $Id$
++ */
++/***********************************************************
++        Copyright 1999 by Carnegie Mellon University
++
++                      All Rights Reserved
++
++Permission to use, copy, modify, and distribute this software and its
++documentation for any purpose and without fee is hereby granted,
++provided that the above copyright notice appear in all copies and that
++both that copyright notice and this permission notice appear in
++supporting documentation, and that the name of Carnegie Mellon
++University not be used in advertising or publicity pertaining to
++distribution of the software without specific, written prior
++permission.
++
++CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
++FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
++ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
++OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++******************************************************************/
++
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include <stdlib.h>
++#include <assert.h>
++#include <string.h>
++#include <ctype.h>
++#include "xmalloc.h"
++#include "comparator.h"
++#include "interp.h"
++#include "script.h"
++#include "tree.h"
++
++#include "imparse.h"
++#include "libconfig.h"
++
++    /* definitions */
++    extern int addrparse(void);
++
++struct vtags {
++    int days;
++    stringlist_t *addresses;
++    char *subject;
++    int mime;
++};
++
++struct htags {
++    char *comparator;
++    int comptag;
++    int relation;
++};
++
++struct aetags {
++    int addrtag;
++    char *comparator;
++    int comptag;
++    int relation;
++};
++
++struct ntags {
++    char *method;
++    char *id;
++    stringlist_t *options;
++    int priority;
++    char *message;
++};
++
++struct dtags {
++    int comptag;
++    int relation;
++    void *pattern;
++    int priority;
++};
++
++static commandlist_t *ret;
++static sieve_script_t *parse_script;
++static int check_reqs(stringlist_t *sl);
++static test_t *build_address(int t, struct aetags *ae,
++			     stringlist_t *sl, stringlist_t *pl);
++static test_t *build_header(int t, struct htags *h,
++			    stringlist_t *sl, stringlist_t *pl);
++static commandlist_t *build_vacation(int t, struct vtags *h, char *s);
++static commandlist_t *build_notify(int t, struct ntags *n);
++static commandlist_t *build_denotify(int t, struct dtags *n);
++static struct aetags *new_aetags(void);
++static struct aetags *canon_aetags(struct aetags *ae);
++static void free_aetags(struct aetags *ae);
++static struct htags *new_htags(void);
++static struct htags *canon_htags(struct htags *h);
++static void free_htags(struct htags *h);
++static struct vtags *new_vtags(void);
++static struct vtags *canon_vtags(struct vtags *v);
++static void free_vtags(struct vtags *v);
++static struct ntags *new_ntags(void);
++static struct ntags *canon_ntags(struct ntags *n);
++static void free_ntags(struct ntags *n);
++static struct dtags *new_dtags(void);
++static struct dtags *canon_dtags(struct dtags *d);
++static void free_dtags(struct dtags *d);
++
++static int verify_stringlist(stringlist_t *sl, int (*verify)(char *));
++static int verify_mailbox(char *s);
++static int verify_address(char *s);
++static int verify_header(char *s);
++static int verify_addrheader(char *s);
++static int verify_envelope(char *s);
++static int verify_flag(char *s);
++static int verify_relat(char *s);
++#ifdef ENABLE_REGEX
++static int verify_regex(char *s, int cflags);
++static int verify_regexs(stringlist_t *sl, char *comp);
++#endif
++static int verify_utf8(char *s);
++
++int yyerror(char *msg);
++extern int yylex(void);
++extern void yyrestart(FILE *f);
++
++#define YYERROR_VERBOSE /* i want better error messages! */
++%}
++
++%union {
++    int nval;
++    char *sval;
++    stringlist_t *sl;
++    test_t *test;
++    testlist_t *testl;
++    commandlist_t *cl;
++    struct vtags *vtag;
++    struct aetags *aetag;
++    struct htags *htag;
++    struct ntags *ntag;
++    struct dtags *dtag;
++}
++
++%token <nval> NUMBER
++%token <sval> STRING
++%token IF ELSIF ELSE
++%token REJCT FILEINTO REDIRECT KEEP STOP DISCARD VACATION REQUIRE
++%token SETFLAG ADDFLAG REMOVEFLAG MARK UNMARK
++%token NOTIFY DENOTIFY
++%token ANYOF ALLOF EXISTS SFALSE STRUE HEADER NOT SIZE ADDRESS ENVELOPE
++%token COMPARATOR IS CONTAINS MATCHES REGEX COUNT VALUE OVER UNDER
++%token GT GE LT LE EQ NE
++%token ALL LOCALPART DOMAIN USER DETAIL
++%token DAYS ADDRESSES SUBJECT MIME
++%token METHOD ID OPTIONS LOW NORMAL HIGH ANY MESSAGE
++
++%type <cl> commands command action elsif block
++%type <sl> stringlist strings
++%type <test> test
++%type <nval> comptag relcomp sizetag addrparttag addrorenv
++%type <testl> testlist tests
++%type <htag> htags
++%type <aetag> aetags
++%type <vtag> vtags
++%type <ntag> ntags
++%type <dtag> dtags
++%type <nval> priority
++
++%%
++
++start: reqs			{ ret = NULL; }
++	| reqs commands		{ ret = $2; }
++	;
++
++reqs: /* empty */
++	| require reqs
++	;
++
++require: REQUIRE stringlist ';'	{ if (!check_reqs($2)) {
++                                    yyerror("Unsupported features in require line");
++				    YYERROR; 
++                                  } }
++	;
++
++commands: command		{ $$ = $1; }
++	| command commands	{ $1->next = $2; $$ = $1; }
++	;
++
++command: action ';'		{ $$ = $1; }
++	| IF test block elsif   { $$ = new_if($2, $3, $4); }
++	| error ';'		{ $$ = new_command(STOP); }
++	;
++
++elsif: /* empty */               { $$ = NULL; }
++	| ELSIF test block elsif { $$ = new_if($2, $3, $4); }
++	| ELSE block             { $$ = $2; }
++	;
++
++action: REJCT STRING             { if (!parse_script->support.reject) {
++				     yyerror("reject require missing");
++				     YYERROR;
++				   }
++				   if (!verify_utf8($2)) {
++				     YYERROR; /* vu should call yyerror() */
++				   }
++				   $$ = new_command(REJCT);
++				   $$->u.str = $2; }
++	| FILEINTO STRING	 { if (!parse_script->support.fileinto) {
++				     yyerror("fileinto require missing");
++	                             YYERROR;
++                                   }
++				   if (!verify_mailbox($2)) {
++				     YYERROR; /* vm should call yyerror() */
++				   }
++	                           $$ = new_command(FILEINTO);
++				   $$->u.str = $2; }
++	| REDIRECT STRING         { $$ = new_command(REDIRECT);
++				   if (!verify_address($2)) {
++				     YYERROR; /* va should call yyerror() */
++				   }
++				   $$->u.str = $2; }
++	| KEEP			 { $$ = new_command(KEEP); }
++	| STOP			 { $$ = new_command(STOP); }
++	| DISCARD		 { $$ = new_command(DISCARD); }
++	| VACATION vtags STRING  { if (!parse_script->support.vacation) {
++				     yyerror("vacation require missing");
++				     YYERROR;
++				   }
++				   if (($2->mime == -1) && !verify_utf8($3)) {
++				     YYERROR; /* vu should call yyerror() */
++				   }
++  				   $$ = build_vacation(VACATION,
++					    canon_vtags($2), $3); }
++        | SETFLAG stringlist     { if (!parse_script->support.imapflags) {
++                                    yyerror("imapflags require missing");
++                                    YYERROR;
++                                   }
++                                  if (!verify_stringlist($2, verify_flag)) {
++                                    YYERROR; /* vf should call yyerror() */
++                                  }
++                                  $$ = new_command(SETFLAG);
++                                  $$->u.sl = $2; }
++         | ADDFLAG stringlist     { if (!parse_script->support.imapflags) {
++                                    yyerror("imapflags require missing");
++                                    YYERROR;
++                                    }
++                                  if (!verify_stringlist($2, verify_flag)) {
++                                    YYERROR; /* vf should call yyerror() */
++                                  }
++                                  $$ = new_command(ADDFLAG);
++                                  $$->u.sl = $2; }
++         | REMOVEFLAG stringlist  { if (!parse_script->support.imapflags) {
++                                    yyerror("imapflags require missing");
++                                    YYERROR;
++                                    }
++                                  if (!verify_stringlist($2, verify_flag)) {
++                                    YYERROR; /* vf should call yyerror() */
++                                  }
++                                  $$ = new_command(REMOVEFLAG);
++                                  $$->u.sl = $2; }
++         | MARK                   { if (!parse_script->support.imapflags) {
++                                    yyerror("imapflags require missing");
++                                    YYERROR;
++                                    }
++                                  $$ = new_command(MARK); }
++         | UNMARK                 { if (!parse_script->support.imapflags) {
++                                    yyerror("imapflags require missing");
++                                    YYERROR;
++                                    }
++                                  $$ = new_command(UNMARK); }
++
++         | NOTIFY ntags           { if (!parse_script->support.notify) {
++				       yyerror("notify require missing");
++				       $$ = new_command(NOTIFY); 
++				       YYERROR;
++	 			    } else {
++				      $$ = build_notify(NOTIFY,
++				             canon_ntags($2));
++				    } }
++         | DENOTIFY dtags         { if (!parse_script->support.notify) {
++                                       yyerror("notify require missing");
++				       $$ = new_command(DENOTIFY);
++				       YYERROR;
++				    } else {
++					$$ = build_denotify(DENOTIFY, canon_dtags($2));
++					if ($$ == NULL) { 
++			yyerror("unable to find a compatible comparator");
++			YYERROR; } } }
++	;
++
++ntags: /* empty */		 { $$ = new_ntags(); }
++	| ntags ID STRING	 { if ($$->id != NULL) { 
++					yyerror("duplicate :method"); YYERROR; }
++				   else { $$->id = $3; } }
++	| ntags METHOD STRING	 { if ($$->method != NULL) { 
++					yyerror("duplicate :method"); YYERROR; }
++				   else { $$->method = $3; } }
++	| ntags OPTIONS stringlist { if ($$->options != NULL) { 
++					yyerror("duplicate :options"); YYERROR; }
++				     else { $$->options = $3; } }
++        | ntags priority	 { if ($$->priority != -1) { 
++                                 yyerror("duplicate :priority"); YYERROR; }
++                                   else { $$->priority = $2; } }
++	| ntags MESSAGE STRING	 { if ($$->message != NULL) { 
++					yyerror("duplicate :message"); YYERROR; }
++				   else { $$->message = $3; } }
++	;
++
++dtags: /* empty */		 { $$ = new_dtags(); }
++	| dtags priority	 { if ($$->priority != -1) { 
++				yyerror("duplicate priority level"); YYERROR; }
++				   else { $$->priority = $2; } }
++	| dtags comptag STRING 	 { if ($$->comptag != -1)
++	                             { 
++					 yyerror("duplicate comparator type tag"); YYERROR;
++				     }
++	                           $$->comptag = $2;
++#ifdef ENABLE_REGEX
++				   if ($$->comptag == REGEX)
++				   {
++				       int cflags = REG_EXTENDED |
++					   REG_NOSUB | REG_ICASE;
++				       if (!verify_regex($3, cflags)) { YYERROR; }
++				   }
++#endif
++				   $$->pattern = $3;
++	                          }
++	| dtags relcomp STRING  { $$ = $1;
++				   if ($$->comptag != -1) { 
++			yyerror("duplicate comparator type tag"); YYERROR; }
++				   else { $$->comptag = $2;
++				   $$->relation = verify_relat($3);
++				   if ($$->relation==-1) 
++				     {YYERROR; /*vr called yyerror()*/ }
++				   } }
++	;
++
++priority: LOW                   { $$ = LOW; }
++        | NORMAL                { $$ = NORMAL; }
++        | HIGH                  { $$ = HIGH; }
++        ;
++
++vtags: /* empty */		 { $$ = new_vtags(); }
++	| vtags DAYS NUMBER	 { if ($$->days != -1) { 
++					yyerror("duplicate :days"); YYERROR; }
++				   else { $$->days = $3; } }
++	| vtags ADDRESSES stringlist { if ($$->addresses != NULL) { 
++					yyerror("duplicate :addresses"); 
++					YYERROR;
++				       } else if (!verify_stringlist($3,
++							verify_address)) {
++					  YYERROR;
++				       } else {
++					 $$->addresses = $3; } }
++	| vtags SUBJECT STRING	 { if ($$->subject != NULL) { 
++					yyerror("duplicate :subject"); 
++					YYERROR;
++				   } else if (!verify_utf8($3)) {
++				        YYERROR; /* vu should call yyerror() */
++				   } else { $$->subject = $3; } }
++	| vtags MIME		 { if ($$->mime != -1) { 
++					yyerror("duplicate :mime"); 
++					YYERROR; }
++				   else { $$->mime = MIME; } }
++	;
++
++stringlist: '[' strings ']'      { $$ = $2; }
++	| STRING		 { $$ = new_sl($1, NULL); }
++	;
++
++strings: STRING			 { $$ = new_sl($1, NULL); }
++	| STRING ',' strings	 { $$ = new_sl($1, $3); }
++	;
++
++block: '{' commands '}'		 { $$ = $2; }
++	| '{' '}'		 { $$ = NULL; }
++	;
++
++test:     ANYOF testlist	 { $$ = new_test(ANYOF); $$->u.tl = $2; }
++        | ALLOF testlist	 { $$ = new_test(ALLOF); $$->u.tl = $2; }
++        | EXISTS stringlist      { $$ = new_test(EXISTS); $$->u.sl = $2; }
++        | SFALSE		 { $$ = new_test(SFALSE); }
++	| STRUE			 { $$ = new_test(STRUE); }
++	| HEADER htags stringlist stringlist
++				 {
++				     if (!verify_stringlist($3, verify_header)) {
++					 YYERROR; /* vh should call yyerror() */
++				     }
++				     if (!verify_stringlist($4, verify_utf8)) {
++					 YYERROR; /* vu should call yyerror() */
++				     }
++				     
++				     $2 = canon_htags($2);
++#ifdef ENABLE_REGEX
++				     if ($2->comptag == REGEX)
++				     {
++					 if (!(verify_regexs($4, $2->comparator)))
++					 { YYERROR; }
++				     }
++#endif
++				     $$ = build_header(HEADER, $2, $3, $4);
++				     if ($$ == NULL) { 
++					 yyerror("unable to find a compatible comparator");
++					 YYERROR; } 
++				 }
++
++
++        | addrorenv aetags stringlist stringlist
++				 { 
++				     if (($1 == ADDRESS) &&
++					 !verify_stringlist($3, verify_addrheader))
++					 { YYERROR; }
++				     else if (($1 == ENVELOPE) &&
++					      !verify_stringlist($3, verify_envelope))
++					 { YYERROR; }
++				     $2 = canon_aetags($2);
++#ifdef ENABLE_REGEX
++				     if ($2->comptag == REGEX)
++				     {
++					 if (!( verify_regexs($4, $2->comparator)))
++					 { YYERROR; }
++				     }
++#endif
++				     $$ = build_address($1, $2, $3, $4);
++				     if ($$ == NULL) { 
++					 yyerror("unable to find a compatible comparator");
++					 YYERROR; } 
++				 }
++
++	| NOT test		 { $$ = new_test(NOT); $$->u.t = $2; }
++	| SIZE sizetag NUMBER    { $$ = new_test(SIZE); $$->u.sz.t = $2;
++		                   $$->u.sz.n = $3; }
++	| error			 { $$ = NULL; }
++	;
++
++addrorenv: ADDRESS		 { $$ = ADDRESS; }
++	| ENVELOPE		 {if (!parse_script->support.envelope)
++	                              {yyerror("envelope require missing"); YYERROR;}
++	                          else{$$ = ENVELOPE; }
++	                         }
++
++	;
++
++aetags: /* empty */              { $$ = new_aetags(); }
++        | aetags addrparttag	 { $$ = $1;
++				   if ($$->addrtag != -1) { 
++			yyerror("duplicate or conflicting address part tag");
++			YYERROR; }
++				   else { $$->addrtag = $2; } }
++	| aetags comptag         { $$ = $1;
++				   if ($$->comptag != -1) { 
++			yyerror("duplicate comparator type tag"); YYERROR; }
++				   else { $$->comptag = $2; } }
++	| aetags relcomp STRING{ $$ = $1;
++				   if ($$->comptag != -1) { 
++			yyerror("duplicate comparator type tag"); YYERROR; }
++				   else { $$->comptag = $2;
++				   $$->relation = verify_relat($3);
++				   if ($$->relation==-1) 
++				     {YYERROR; /*vr called yyerror()*/ }
++				   } }
++        | aetags COMPARATOR STRING { $$ = $1;
++	if ($$->comparator != NULL) { 
++			yyerror("duplicate comparator tag"); YYERROR; }
++				   else if (!strcmp($3, "i;ascii-numeric") &&
++					    !parse_script->support.i_ascii_numeric) {
++			yyerror("comparator-i;ascii-numeric require missing");
++			YYERROR; }
++				   else { $$->comparator = $3; } }
++	;
++
++htags: /* empty */		 { $$ = new_htags(); }
++	| htags comptag		 { $$ = $1;
++				   if ($$->comptag != -1) { 
++			yyerror("duplicate comparator type tag"); YYERROR; }
++				   else { $$->comptag = $2; } }
++	| htags relcomp STRING { $$ = $1;
++				   if ($$->comptag != -1) { 
++			yyerror("duplicate comparator type tag"); YYERROR; }
++				   else { $$->comptag = $2;
++				   $$->relation = verify_relat($3);
++				   if ($$->relation==-1) 
++				     {YYERROR; /*vr called yyerror()*/ }
++				   } }
++	| htags COMPARATOR STRING { $$ = $1;
++				   if ($$->comparator != NULL) { 
++			 yyerror("duplicate comparator tag"); YYERROR; }
++				   else if (!strcmp($3, "i;ascii-numeric") &&
++					    !parse_script->support.i_ascii_numeric) { 
++			 yyerror("comparator-i;ascii-numeric require missing");  YYERROR; }
++				   else { 
++				     $$->comparator = $3; } }
++        ;
++
++
++addrparttag: ALL                 { $$ = ALL; }
++	| LOCALPART		 { $$ = LOCALPART; }
++	| DOMAIN                 { $$ = DOMAIN; }
++	| USER                   { if (!parse_script->support.subaddress) {
++				     yyerror("subaddress require missing");
++				     YYERROR;
++				   }
++				   $$ = USER; }  
++	| DETAIL                { if (!parse_script->support.subaddress) {
++				     yyerror("subaddress require missing");
++				     YYERROR;
++				   }
++				   $$ = DETAIL; }
++	;
++comptag: IS			 { $$ = IS; }
++	| CONTAINS		 { $$ = CONTAINS; }
++	| MATCHES		 { $$ = MATCHES; }
++	| REGEX			 { if (!parse_script->support.regex) {
++				     yyerror("regex require missing");
++				     YYERROR;
++				   }
++				   $$ = REGEX; }
++	;
++
++relcomp: COUNT			 { if (!parse_script->support.relational) {
++				     yyerror("relational require missing");
++				     YYERROR;
++				   }
++				   $$ = COUNT; }
++	| VALUE			 { if (!parse_script->support.relational) {
++				     yyerror("relational require missing");
++				     YYERROR;
++				   }
++				   $$ = VALUE; }
++	;
++
++
++sizetag: OVER			 { $$ = OVER; }
++	| UNDER			 { $$ = UNDER; }
++	;
++
++testlist: '(' tests ')'		 { $$ = $2; }
++	;
++
++tests: test                      { $$ = new_testlist($1, NULL); }
++	| test ',' tests         { $$ = new_testlist($1, $3); }
++	;
++
++%%
++commandlist_t *sieve_parse(sieve_script_t *script, FILE *f)
++{
++    commandlist_t *t;
++
++    parse_script = script;
++    yyrestart(f);
++    if (yyparse()) {
++	t = NULL;
++    } else {
++	t = ret;
++    }
++    ret = NULL;
++    return t;
++}
++
++int yyerror(char *msg)
++{
++    extern int yylineno;
++    int ret;
++
++    parse_script->err++;
++    if (parse_script->interp.err) {
++	ret = parse_script->interp.err(yylineno, msg, 
++				       parse_script->interp.interp_context,
++				       parse_script->script_context);
++    }
++
++    return 0;
++}
++
++static int check_reqs(stringlist_t *sl)
++{
++    int i = 1;
++    stringlist_t *s;
++    
++    while (sl != NULL) {
++	s = sl;
++	sl = sl->next;
++
++	i &= script_require(parse_script, s->s);
++
++	if (s->s) free(s->s);
++	free(s);
++    }
++    return i;
++}
++
++static test_t *build_address(int t, struct aetags *ae,
++			     stringlist_t *sl, stringlist_t *pl)
++{
++    test_t *ret = new_test(t);	/* can be either ADDRESS or ENVELOPE */
++
++    assert((t == ADDRESS) || (t == ENVELOPE));
++
++    if (ret) {
++	ret->u.ae.comptag = ae->comptag;
++	ret->u.ae.relation=ae->relation;
++	ret->u.ae.comparator=strdup(ae->comparator);
++	ret->u.ae.sl = sl;
++	ret->u.ae.pl = pl;
++	ret->u.ae.addrpart = ae->addrtag;
++	free_aetags(ae);
++
++    }
++    return ret;
++}
++
++static test_t *build_header(int t, struct htags *h,
++			    stringlist_t *sl, stringlist_t *pl)
++{
++    test_t *ret = new_test(t);	/* can be HEADER */
++
++    assert(t == HEADER);
++
++    if (ret) {
++	ret->u.h.comptag = h->comptag;
++	ret->u.h.relation=h->relation;
++	ret->u.h.comparator=strdup(h->comparator);
++	ret->u.h.sl = sl;
++	ret->u.h.pl = pl;
++	free_htags(h);
++    }
++    return ret;
++}
++
++static commandlist_t *build_vacation(int t, struct vtags *v, char *reason)
++{
++    commandlist_t *ret = new_command(t);
++
++    assert(t == VACATION);
++
++    if (ret) {
++	ret->u.v.subject = v->subject; v->subject = NULL;
++	ret->u.v.days = v->days;
++	ret->u.v.mime = v->mime;
++	ret->u.v.addresses = v->addresses; v->addresses = NULL;
++	free_vtags(v);
++	ret->u.v.message = reason;
++    }
++    return ret;
++}
++
++static commandlist_t *build_notify(int t, struct ntags *n)
++{
++    commandlist_t *ret = new_command(t);
++
++    assert(t == NOTIFY);
++       if (ret) {
++	ret->u.n.method = n->method; n->method = NULL;
++	ret->u.n.id = n->id; n->id = NULL;
++	ret->u.n.options = n->options; n->options = NULL;
++	ret->u.n.priority = n->priority;
++	ret->u.n.message = n->message; n->message = NULL;
++	free_ntags(n);
++    }
++    return ret;
++}
++
++static commandlist_t *build_denotify(int t, struct dtags *d)
++{
++    commandlist_t *ret = new_command(t);
++
++    assert(t == DENOTIFY);
++
++    if (ret) {
++	ret->u.d.comptag = d->comptag;
++	ret->u.d.relation=d->relation;
++	ret->u.d.pattern = d->pattern; d->pattern = NULL;
++	ret->u.d.priority = d->priority;
++	free_dtags(d);
++    }
++    return ret;
++}
++
++static struct aetags *new_aetags(void)
++{
++    struct aetags *r = (struct aetags *) xmalloc(sizeof(struct aetags));
++
++    r->addrtag = r->comptag = r->relation=-1;
++    r->comparator=NULL;
++
++    return r;
++}
++
++static struct aetags *canon_aetags(struct aetags *ae)
++{
++    if (ae->addrtag == -1) { ae->addrtag = ALL; }
++    if (ae->comparator == NULL) {
++        ae->comparator = xstrdup("i;ascii-casemap");
++    }
++    if (ae->comptag == -1) { ae->comptag = IS; }
++    return ae;
++}
++
++static void free_aetags(struct aetags *ae)
++{
++    free(ae->comparator);
++     free(ae);
++}
++
++static struct htags *new_htags(void)
++{
++    struct htags *r = (struct htags *) xmalloc(sizeof(struct htags));
++
++    r->comptag = r->relation= -1;
++    
++    r->comparator = NULL;
++
++    return r;
++}
++
++static struct htags *canon_htags(struct htags *h)
++{
++    if (h->comparator == NULL) {
++	h->comparator = xstrdup("i;ascii-casemap");
++    }
++    if (h->comptag == -1) { h->comptag = IS; }
++    return h;
++}
++
++static void free_htags(struct htags *h)
++{
++    free(h->comparator);
++    free(h);
++}
++
++static struct vtags *new_vtags(void)
++{
++    struct vtags *r = (struct vtags *) xmalloc(sizeof(struct vtags));
++
++    r->days = -1;
++    r->addresses = NULL;
++    r->subject = NULL;
++    r->mime = -1;
++
++    return r;
++}
++
++static struct vtags *canon_vtags(struct vtags *v)
++{
++    assert(parse_script->interp.vacation != NULL);
++
++    if (v->days == -1) { v->days = 7; }
++    if (v->days < parse_script->interp.vacation->min_response) 
++       { v->days = parse_script->interp.vacation->min_response; }
++    if (v->days > parse_script->interp.vacation->max_response)
++       { v->days = parse_script->interp.vacation->max_response; }
++    if (v->mime == -1) { v->mime = 0; }
++
++    return v;
++}
++
++static void free_vtags(struct vtags *v)
++{
++    if (v->addresses) { free_sl(v->addresses); }
++    if (v->subject) { free(v->subject); }
++    free(v);
++}
++
++static struct ntags *new_ntags(void)
++{
++    struct ntags *r = (struct ntags *) xmalloc(sizeof(struct ntags));
++
++    r->method = NULL;
++    r->id = NULL;
++    r->options = NULL;
++    r->priority = -1;
++    r->message = NULL;
++
++    return r;
++}
++
++static struct ntags *canon_ntags(struct ntags *n)
++{
++    if (n->priority == -1) { n->priority = NORMAL; }
++    if (n->message == NULL) { n->message = strdup("$from$: $subject$"); }
++    if (n->method == NULL) { n->method = strdup("default"); }
++    return n;
++}
++static struct dtags *canon_dtags(struct dtags *d)
++{
++    if (d->priority == -1) { d->priority = ANY; }
++    if (d->comptag == -1) { d->comptag = ANY; }
++       return d;
++}
++
++static void free_ntags(struct ntags *n)
++{
++    if (n->method) { free(n->method); }
++    if (n->id) { free(n->id); }
++    if (n->options) { free_sl(n->options); }
++    if (n->message) { free(n->message); }
++    free(n);
++}
++
++static struct dtags *new_dtags(void)
++{
++    struct dtags *r = (struct dtags *) xmalloc(sizeof(struct dtags));
++
++    r->comptag = r->priority= r->relation = -1;
++    r->pattern  = NULL;
++
++    return r;
++}
++
++static void free_dtags(struct dtags *d)
++{
++    if (d->pattern) free(d->pattern);
++    free(d);
++}
++
++static int verify_stringlist(stringlist_t *sl, int (*verify)(char *))
++{
++    for (; sl != NULL && verify(sl->s); sl = sl->next) ;
++    return (sl == NULL);
++}
++
++char *addrptr;		/* pointer to address string for address lexer */
++char addrerr[500];	/* buffer for address parser error messages */
++
++static int verify_address(char *s)
++{
++    char errbuf[500];
++
++    addrptr = s;
++    addrerr[0] = '\0';	/* paranoia */
++    if (addrparse()) {
++	snprintf(errbuf, sizeof(errbuf), "address '%s': %s", s, addrerr);
++	yyerror(errbuf);
++	return 0;
++    }
++    return 1;
++}
++
++static int verify_mailbox(char *s)
++{
++    if (!verify_utf8(s)) return 0;
++
++    /* xxx if not a mailbox, call yyerror */
++    return 1;
++}
++
++static int verify_header(char *hdr)
++{
++    char *h = hdr;
++    char errbuf[100];
++
++    while (*h) {
++	/* field-name      =       1*ftext
++	   ftext           =       %d33-57 / %d59-126         
++	   ; Any character except
++	   ;  controls, SP, and
++	   ;  ":". */
++	if (!((*h >= 33 && *h <= 57) || (*h >= 59 && *h <= 126))) {
++	    snprintf(errbuf, sizeof(errbuf),
++		     "header '%s': not a valid header", hdr);
++	    yyerror(errbuf);
++	    return 0;
++	}
++	h++;
++    }
++    return 1;
++}
++ 
++static int verify_addrheader(char *hdr)
++{
++    const char **h, *hdrs[] = {
++	"from", "sender", "reply-to",	/* RFC2822 originator fields */
++	"to", "cc", "bcc",		/* RFC2822 destination fields */
++	"resent-from", "resent-sender",	/* RFC2822 resent fields */
++	"resent-to", "resent-cc", "resent-bcc",
++	"return-path",			/* RFC2822 trace fields */
++	"disposition-notification-to",	/* RFC2298 MDN request fields */
++	"delivered-to",			/* non-standard (loop detection) */
++	"approved",			/* RFC1036 moderator/control fields */
++	NULL
++    };
++    char errbuf[100];
++
++    if (!config_getswitch(IMAPOPT_RFC3028_STRICT))
++	return verify_header(hdr);
++
++    for (lcase(hdr), h = hdrs; *h; h++) {
++	if (!strcmp(*h, hdr)) return 1;
++    }
++
++    snprintf(errbuf, sizeof(errbuf),
++	     "header '%s': not a valid header for an address test", hdr);
++    yyerror(errbuf);
++    return 0;
++}
++ 
++static int verify_envelope(char *env)
++{
++    char errbuf[100];
++
++    lcase(env);
++    if (!config_getswitch(IMAPOPT_RFC3028_STRICT) ||
++	!strcmp(env, "from") || !strcmp(env, "to") || !strcmp(env, "auth")) {
++	return 1;
++    }
++
++    snprintf(errbuf, sizeof(errbuf),
++	     "env-part '%s': not a valid part for an envelope test", env);
++    yyerror(errbuf);
++    return 0;
++}
++ 
++static int verify_relat(char *r)
++{/* this really should have been a token to begin with.*/
++    char errbuf[100];
++	lcase(r);
++	if (!strcmp(r, "gt")) {return GT;}
++	else if (!strcmp(r, "ge")) {return GE;}
++	else if (!strcmp(r, "lt")) {return LT;}
++	else if (!strcmp(r, "le")) {return LE;}
++	else if (!strcmp(r, "ne")) {return NE;}
++	else if (!strcmp(r, "eq")) {return EQ;}
++	else{
++	  sprintf(errbuf, "flag '%s': not a valid relational operation", r);
++	  yyerror(errbuf);
++	  return -1;
++	}
++	
++}
++
++
++
++
++static int verify_flag(char *f)
++{
++    char errbuf[100];
++ 
++    if (f[0] == '\\') {
++	lcase(f);
++	if (strcmp(f, "\\seen") && strcmp(f, "\\answered") &&
++	    strcmp(f, "\\flagged") && strcmp(f, "\\draft") &&
++	    strcmp(f, "\\deleted")) {
++	    snprintf(errbuf, sizeof(errbuf),
++		     "flag '%s': not a system flag", f);
++	    yyerror(errbuf);
++	    return 0;
++	}
++	return 1;
++    }
++    if (!imparse_isatom(f)) {
++	snprintf(errbuf, sizeof(errbuf), "flag '%s': not a valid keyword", f);
++	yyerror(errbuf);
++	return 0;
++    }
++    return 1;
++}
++ 
++#ifdef ENABLE_REGEX
++static int verify_regex(char *s, int cflags)
++{
++    int ret;
++    char errbuf[100];
++    regex_t *reg = (regex_t *) xmalloc(sizeof(regex_t));
++
++     if ((ret = regcomp(reg, s, cflags)) != 0) {
++	(void) regerror(ret, reg, errbuf, sizeof(errbuf));
++	yyerror(errbuf);
++	free(reg);
++	return 0;
++	}
++    free(reg);
++    return 1;
++}
++
++static int verify_regexs(stringlist_t *sl, char *comp)
++{
++    stringlist_t *sl2;
++    int cflags = REG_EXTENDED | REG_NOSUB;
++ 
++
++    if (!strcmp(comp, "i;ascii-casemap")) {
++	cflags |= REG_ICASE;
++    }
++
++    for (sl2 = sl; sl2 != NULL; sl2 = sl2->next) {
++	if ((verify_regex(sl2->s, cflags)) == 0) {
++	    break;
++	}
++    }
++    if (sl2 == NULL) {
++	return 1;
++    }
++    return 0;
++}
++#endif
++
++/*
++ * Valid UTF-8 check (from RFC 2640 Annex B.1)
++ *
++ * The following routine checks if a byte sequence is valid UTF-8. This
++ * is done by checking for the proper tagging of the first and following
++ * bytes to make sure they conform to the UTF-8 format. It then checks
++ * to assure that the data part of the UTF-8 sequence conforms to the
++ * proper range allowed by the encoding. Note: This routine will not
++ * detect characters that have not been assigned and therefore do not
++ * exist.
++ */
++static int verify_utf8(char *s)
++{
++    const unsigned char *buf = (const unsigned char *)s;
++    const unsigned char *endbuf = buf + strlen(s);
++    unsigned char byte2mask = 0x00, c;
++    int trailing = 0;  /* trailing (continuation) bytes to follow */
++
++    while (buf != endbuf) {
++	c = *buf++;
++	if (trailing) {
++	    if ((c & 0xC0) == 0x80) {		/* Does trailing byte
++						   follow UTF-8 format? */
++		if (byte2mask) {		/* Need to check 2nd byte
++						   for proper range? */
++		    if (c & byte2mask)		/* Are appropriate bits set? */
++			byte2mask = 0x00;
++		    else
++			break;
++		}
++		trailing--;
++	    }
++	    else
++		break;
++	}
++	else {
++	    if ((c & 0x80) == 0x00)		/* valid 1 byte UTF-8 */
++		continue;
++	    else if ((c & 0xE0) == 0xC0)	/* valid 2 byte UTF-8 */
++		if (c & 0x1E) {			/* Is UTF-8 byte
++						   in proper range? */
++		    trailing = 1;
++		}
++		else
++		    break;
++	    else if ((c & 0xF0) == 0xE0) {	/* valid 3 byte UTF-8 */
++		if (!(c & 0x0F)) {		/* Is UTF-8 byte
++						   in proper range? */
++		    byte2mask = 0x20;		/* If not, set mask
++						   to check next byte */
++		}
++		trailing = 2;
++	    }
++	    else if ((c & 0xF8) == 0xF0) {	/* valid 4 byte UTF-8 */
++		if (!(c & 0x07)) {		/* Is UTF-8 byte
++						   in proper range? */
++		    byte2mask = 0x30;		/* If not, set mask
++						   to check next byte */
++		}
++		trailing = 3;
++	    }
++	    else if ((c & 0xFC) == 0xF8) {	/* valid 5 byte UTF-8 */
++		if (!(c & 0x03)) {		/* Is UTF-8 byte
++						   in proper range? */
++		    byte2mask = 0x38;		/* If not, set mask
++						   to check next byte */
++		}
++		trailing = 4;
++	    }
++	    else if ((c & 0xFE) == 0xFC) {	/* valid 6 byte UTF-8 */
++		if (!(c & 0x01)) {		/* Is UTF-8 byte
++						   in proper range? */
++		    byte2mask = 0x3C;		/* If not, set mask
++						   to check next byte */
++		}
++		trailing = 5;
++	    }
++	    else
++		break;
++	}
++    }
++
++    if ((buf != endbuf) || trailing) {
++	char errbuf[100];
++
++	snprintf(errbuf, sizeof(errbuf),
++		 "string '%s': not valid utf8", s);
++	yyerror(errbuf);
++	return 0;
++    }
++
++    return 1;
++}
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/sieve_err.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/sieve_err.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,60 @@
++/*
++ * sieve_err.c:
++ * This file is automatically generated; please do not edit it.
++ */
++
++#include <stdlib.h>
++
++static const char * const text[] = {
++	   "Generic Sieve error",
++	   "Sieve interpretor not finalized",
++	   "Parse error in Sieve script",
++	   "Run-time error during Sieve execution",
++	   "Internal error in Sieve subsystem",
++	   "Memory exhausted in Sieve subsystem",
++	   "Sieve action already taken",
++    0
++};
++
++struct error_table {
++    char const * const * msgs;
++    long base;
++    int n_msgs;
++};
++struct et_list {
++    struct et_list *next;
++    const struct error_table * table;
++};
++extern struct et_list *_et_list;
++
++const struct error_table et_siev_error_table = { text, -1237848064L, 7 };
++
++static struct et_list link = { 0, 0 };
++
++void initialize_siev_error_table(void);
++
++void initialize_siev_error_table(void) {
++    if (!link.table) {
++        link.next = _et_list;
++        link.table = &et_siev_error_table;
++        _et_list = &link;
++    }
++}
++
++/* For Heimdal compatibility */
++void initialize_siev_error_table_r(struct et_list **list);
++
++void initialize_siev_error_table_r(struct et_list **list)
++{
++    struct et_list *et, **end;
++
++    for (end = list, et = *list; et; end = &et->next, et = et->next)
++        if (et->table->msgs == text)
++            return;
++    et = malloc(sizeof(struct et_list));
++    if (et == 0)
++        return;
++    et->table = &et_siev_error_table;
++    et->next = 0;
++    *end = et;
++}
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/sieve_err.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/sieve_err.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,24 @@
++/*
++ * sieve_err.h:
++ * This file used to be automatically generated from sieve_er.et.
++ */
++struct et_list;
++
++#define SIEVE_FAIL                               (-1237848064L)
++#define SIEVE_NOT_FINALIZED                      (-1237848063L)
++#define SIEVE_PARSE_ERROR                        (-1237848062L)
++#define SIEVE_RUN_ERROR                          (-1237848061L)
++#define SIEVE_INTERNAL_ERROR                     (-1237848060L)
++#define SIEVE_NOMEM                              (-1237848059L)
++#define SIEVE_DONE                               (-1237848058L)
++extern const struct error_table et_siev_error_table;
++extern void initialize_siev_error_table(void);
++
++/* For compatibility with Heimdal */
++extern void initialize_siev_error_table_r(struct et_list **list);
++
++#define ERROR_TABLE_BASE_siev (-1237848064L)
++
++/* for compatibility with older versions... */
++#define init_siev_err_tbl initialize_siev_error_table
++#define siev_err_base ERROR_TABLE_BASE_siev
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/sieve_interface.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/sieve_interface.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,172 @@
++/* sieve_interface.h -- interface for deliver
++ * $Id$
++ */
++/***********************************************************
++        Copyright 1999 by Carnegie Mellon University
++
++                      All Rights Reserved
++
++Permission to use, copy, modify, and distribute this software and its
++documentation for any purpose and without fee is hereby granted,
++provided that the above copyright notice appear in all copies and that
++both that copyright notice and this permission notice appear in
++supporting documentation, and that the name of Carnegie Mellon
++University not be used in advertising or publicity pertaining to
++distribution of the software without specific, written prior
++permission.
++
++CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
++FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
++ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
++OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++******************************************************************/
++
++#ifndef SIEVE_H
++#define SIEVE_H
++
++#include <stdio.h>
++
++#define SIEVE_VERSION "CMU Sieve 2.2"
++
++/* error codes */
++#define SIEVE_OK (0)
++
++#include "sieve_err.h"
++
++/* external sieve types */
++typedef struct sieve_interp sieve_interp_t;
++typedef struct sieve_script sieve_script_t;
++typedef struct sieve_bytecode sieve_bytecode_t;
++typedef struct bytecode_info bytecode_info_t;
++
++typedef int sieve_callback(void *action_context, void *interp_context, 
++			   void *script_context,
++			   void *message_context, const char **errmsg);
++typedef int sieve_get_size(void *message_context, int *size);
++typedef int sieve_get_header(void *message_context, 
++			     const char *header,
++			     const char ***contents);
++typedef int sieve_get_envelope(void *message_context, 
++			       const char *field,
++			       const char ***contents);
++
++typedef struct sieve_vacation {
++    int min_response;		/* 0 -> defaults to 3 */
++    int max_response;		/* 0 -> defaults to 90 */
++
++    /* given a hash, say whether we've already responded to it in the last
++       days days.  return SIEVE_OK if we SHOULD autorespond (have not already)
++       or SIEVE_DONE if we SHOULD NOT. */
++    sieve_callback *autorespond;
++
++    /* mail the response */
++    sieve_callback *send_response;
++} sieve_vacation_t;
++
++typedef struct sieve_imapflags {
++    char **flag;		/* NULL -> defaults to \flagged */
++    int nflags;
++} sieve_imapflags_t;
++
++typedef struct sieve_redirect_context {
++    const char *addr;
++} sieve_redirect_context_t;
++
++typedef struct sieve_reject_context {
++    const char *msg;
++} sieve_reject_context_t;
++
++typedef struct sieve_fileinto_context {
++    const char *mailbox;
++    sieve_imapflags_t *imapflags;
++} sieve_fileinto_context_t;
++
++typedef struct sieve_keep_context {
++    sieve_imapflags_t *imapflags;
++} sieve_keep_context_t;
++
++typedef struct sieve_notify_context {
++    const char *method;
++    const char **options;
++    const char *priority;
++    const char *message;
++} sieve_notify_context_t;
++
++typedef struct sieve_autorespond_context {
++    unsigned char *hash;
++    int len;
++    int days;
++} sieve_autorespond_context_t;
++
++typedef struct sieve_send_response_context {
++    char *addr;
++    char *fromaddr;
++    const char *msg;
++    char *subj;
++    int mime;
++} sieve_send_response_context_t;
++
++/* build a sieve interpretor */
++int sieve_interp_alloc(sieve_interp_t **interp, void *interp_context);
++int sieve_interp_free(sieve_interp_t **interp);
++
++/* add the callbacks for actions. undefined behavior results if these
++   are called after sieve_script_parse is called! */
++int sieve_register_redirect(sieve_interp_t *interp, sieve_callback *f);
++int sieve_register_discard(sieve_interp_t *interp, sieve_callback *f);
++int sieve_register_reject(sieve_interp_t *interp, sieve_callback *f);
++int sieve_register_fileinto(sieve_interp_t *interp, sieve_callback *f);
++int sieve_register_keep(sieve_interp_t *interp, sieve_callback *f);
++int sieve_register_vacation(sieve_interp_t *interp, sieve_vacation_t *v);
++int sieve_register_imapflags(sieve_interp_t *interp, sieve_imapflags_t *mark);
++int sieve_register_notify(sieve_interp_t *interp, sieve_callback *f);
++
++/* add the callbacks for messages. again, undefined if used after
++   sieve_script_parse */
++int sieve_register_size(sieve_interp_t *interp, sieve_get_size *f);
++int sieve_register_header(sieve_interp_t *interp, sieve_get_header *f);
++int sieve_register_envelope(sieve_interp_t *interp, sieve_get_envelope *f);
++
++typedef int sieve_parse_error(int lineno, const char *msg, 
++			      void *interp_context,
++			      void *script_context);
++int sieve_register_parse_error(sieve_interp_t *interp, sieve_parse_error *f);
++
++typedef int sieve_execute_error(const char *msg, void *interp_context,
++				void *script_context, void *message_context);
++int sieve_register_execute_error(sieve_interp_t *interp, 
++				 sieve_execute_error *f);
++ 
++/* given an interpretor and a script, produce an executable script */
++int sieve_script_parse(sieve_interp_t *interp, FILE *script,
++		       void *script_context, sieve_script_t **ret);
++
++/* given a bytecode file descriptor, setup the sieve_bytecode_t */
++int sieve_script_load(const char *fname, sieve_bytecode_t **ret);
++
++/* Unload a sieve_bytecode_t */
++int sieve_script_unload(sieve_bytecode_t **s);
++
++/* Free a sieve_script_t */
++int sieve_script_free(sieve_script_t **s);
++
++/* execute bytecode on a message */
++int sieve_execute_bytecode(sieve_bytecode_t *script, sieve_interp_t *interp,
++			   void *script_context, void *message_context);
++
++/* Get space separated list of extensions supported by the implementation */
++const char *sieve_listextensions(void);
++
++/* Create a bytecode structure given a parsed commandlist */
++int sieve_generate_bytecode(bytecode_info_t **retval, sieve_script_t *s);
++
++/* Emit bytecode to a file descriptor */
++int sieve_emit_bytecode(int fd, bytecode_info_t *bc);
++
++/* Free a bytecode_info_t */
++void sieve_free_bytecode(bytecode_info_t **p);
++
++#endif
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/sievec.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/sievec.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,273 @@
++/* sievec.c -- compile a sieve script to bytecode manually
++ * Rob Siemborski
++ * $Id$
++ */
++/*
++ * Copyright (c) 1999-2000 Carnegie Mellon University.  All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer. 
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in
++ *    the documentation and/or other materials provided with the
++ *    distribution.
++ *
++ * 3. The name "Carnegie Mellon University" must not be used to
++ *    endorse or promote products derived from this software without
++ *    prior written permission. For permission or any other legal
++ *    details, please contact  
++ *      Office of Technology Transfer
++ *      Carnegie Mellon University
++ *      5000 Forbes Avenue
++ *      Pittsburgh, PA  15213-3890
++ *      (412) 268-4387, fax: (412) 268-7395
++ *      tech-transfer@andrew.cmu.edu
++ *
++ * 4. Redistributions of any form whatsoever must retain the following
++ *    acknowledgment:
++ *    "This product includes software developed by Computing Services
++ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
++ *
++ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
++ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
++ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
++ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
++ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ *
++ */
++
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include "sieve_interface.h"
++
++#include "libconfig.h"
++#include "xmalloc.h"
++
++#include "script.h"
++#include <string.h> 
++#include <stdlib.h>
++#include <sys/file.h>
++#include <unistd.h>
++#include <stdio.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++
++struct et_list *_et_list = NULL;
++
++int is_script_parsable(FILE *stream, char **errstr, sieve_script_t **ret);
++
++#define TIMSIEVE_FAIL -1
++#define TIMSIEVE_OK 0
++
++int main(int argc, char **argv) 
++{
++    FILE *instream;
++    char *err = NULL;
++    sieve_script_t *s;
++    bytecode_info_t *bc;
++    int c, fd, usage_error = 0;
++
++    while ((c = getopt(argc, argv, "C:")) != EOF)
++	switch (c) {
++	default:
++	    usage_error = 1;
++	    break;
++	}
++
++    if (usage_error || (argc - optind) < 2) {
++	printf("Syntax: %s <filename> <outputfile>\n",
++	       argv[0]);
++	exit(1);
++    }
++
++    instream = fopen(argv[optind++],"r");
++    if(instream == NULL) {
++	printf("Unable to open %s for reading\n", argv[1]);
++	exit(1);
++    }
++    
++    if(is_script_parsable(instream, &err, &s) == TIMSIEVE_FAIL) {
++	if(err) {
++	    printf("Unable to parse script: %s\n", err);
++	} else {
++	    printf("Unable to parse script.\n");
++	}
++	 
++	exit(1);
++    }
++    
++    /* Now, generate the bytecode */
++    if(sieve_generate_bytecode(&bc, s) == -1) {
++	printf("bytecode generate failed\n");
++	exit(1);
++    }
++
++    /* Now, open the new file */
++    fd = open(argv[optind], O_CREAT | O_TRUNC | O_WRONLY, 0644);
++    if(fd < 0) {
++	printf("couldn't open bytecode output file\n");
++	exit(1);
++    }  
++
++    /* Now, emit the bytecode */
++    if(sieve_emit_bytecode(fd, bc) == -1) {
++	printf("bytecode emit failed\n");
++	exit(1);
++    }
++
++    close(fd);
++    
++    return 0;
++}
++
++/* to make larry's stupid functions happy :) */ 
++static void foo(void)
++{
++    i_fatal("stub function called");
++}
++sieve_vacation_t vacation = {
++    0,				/* min response */
++    0,				/* max response */
++    (sieve_callback *) &foo,	/* autorespond() */
++    (sieve_callback *) &foo	/* send_response() */
++};
++
++static int sieve_notify(void *ac __attr_unused__, 
++			void *interp_context __attr_unused__, 
++			void *script_context __attr_unused__,
++			void *message_context __attr_unused__,
++			const char **errmsg __attr_unused__)
++{
++    i_fatal("stub function called");
++    return SIEVE_FAIL;
++}
++
++static int mysieve_error(int lineno, const char *msg,
++			 void *i __attr_unused__, void *s)
++{
++    char buf[1024];
++    char **errstr = (char **) s;
++
++    snprintf(buf, 80, "line %d: %s\r\n", lineno, msg);
++    *errstr = xrealloc(*errstr, strlen(*errstr) + strlen(buf) + 30);
++    i_info("%s", buf);
++    strcat(*errstr, buf);
++
++    return SIEVE_OK;
++}
++
++/* end the boilerplate */
++
++/* returns TRUE or FALSE */
++int is_script_parsable(FILE *stream, char **errstr, sieve_script_t **ret)
++{
++    sieve_interp_t *i;
++    sieve_script_t *s;
++    int res;
++  
++    res = sieve_interp_alloc(&i, NULL);
++    if (res != SIEVE_OK) {
++	i_error("sieve_interp_alloc() returns %d\n", res);
++	return TIMSIEVE_FAIL;
++    }
++
++    res = sieve_register_redirect(i, (sieve_callback *) &foo);
++    if (res != SIEVE_OK) {
++	i_error("sieve_register_redirect() returns %d\n", res);
++	return TIMSIEVE_FAIL;
++    }
++    res = sieve_register_discard(i, (sieve_callback *) &foo);
++    if (res != SIEVE_OK) {
++	i_error("sieve_register_discard() returns %d\n", res);
++	return TIMSIEVE_FAIL;
++    }
++    res = sieve_register_reject(i, (sieve_callback *) &foo);
++    if (res != SIEVE_OK) {
++	i_error("sieve_register_reject() returns %d\n", res);
++	return TIMSIEVE_FAIL;
++    }
++    res = sieve_register_fileinto(i, (sieve_callback *) &foo);
++    if (res != SIEVE_OK) {
++	i_error("sieve_register_fileinto() returns %d\n", res);
++	return TIMSIEVE_FAIL;
++    }
++    res = sieve_register_keep(i, (sieve_callback *) &foo);
++    if (res != SIEVE_OK) {
++	i_error("sieve_register_keep() returns %d\n", res);
++	return TIMSIEVE_FAIL;
++    }
++
++    res = sieve_register_imapflags(i, NULL);
++    if (res != SIEVE_OK) {
++	i_error("sieve_register_imapflags() returns %d\n", res);
++	return TIMSIEVE_FAIL;
++    }
++
++    res = sieve_register_size(i, (sieve_get_size *) &foo);
++    if (res != SIEVE_OK) {
++	i_error("sieve_register_size() returns %d\n", res);
++	return TIMSIEVE_FAIL;
++    }
++  
++    res = sieve_register_header(i, (sieve_get_header *) &foo);
++    if (res != SIEVE_OK) {
++	i_error("sieve_register_header() returns %d\n", res);
++	return TIMSIEVE_FAIL;
++    }
++  
++    res = sieve_register_envelope(i, (sieve_get_envelope *) &foo);
++    if (res != SIEVE_OK) {
++	i_error("sieve_register_envelope() returns %d\n", res);
++	return TIMSIEVE_FAIL;
++    }
++  
++    res = sieve_register_vacation(i, &vacation);
++    if (res != SIEVE_OK) {
++	i_error("sieve_register_vacation() returns %d\n", res);
++	return TIMSIEVE_FAIL;
++    }
++
++    res = sieve_register_notify(i, &sieve_notify);
++    if (res != SIEVE_OK) {
++	i_error("sieve_register_notify() returns %d\n", res);
++	return TIMSIEVE_FAIL;
++    }
++
++    res = sieve_register_parse_error(i, &mysieve_error);
++    if (res != SIEVE_OK) {
++	i_error("sieve_register_parse_error() returns %d\n", res);
++	return TIMSIEVE_FAIL;
++    }
++
++    rewind(stream);
++
++    *errstr = (char *) xmalloc(20 * sizeof(char));
++    strcpy(*errstr, "script errors:\r\n");
++
++    res = sieve_script_parse(i, stream, errstr, &s);
++
++    if (res == SIEVE_OK) {
++	if(ret) {
++	    *ret = s;
++	} else {
++	    sieve_script_free(&s);
++	}
++	free(*errstr);
++	*errstr = NULL;
++    }
++
++    /* free interpreter */
++    sieve_interp_free(&i);
++
++    return (res == SIEVE_OK) ? TIMSIEVE_OK : TIMSIEVE_FAIL;
++}
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/sieved.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/sieved.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,437 @@
++/* dump.c -- bytecode decompiler
++ * Jen Smith
++ */
++/***********************************************************
++        Copyright 1999 by Carnegie Mellon University
++
++                      All Rights Reserved
++
++Permission to use, copy, modify, and distribute this software and its
++documentation for any purpose and without fee is hereby granted,
++provided that the above copyright notice appear in all copies and that
++both that copyright notice and this permission notice appear in
++supporting documentation, and that the name of Carnegie Mellon
++University not be used in advertising or publicity pertaining to
++distribution of the software without specific, written prior
++permission.
++
++CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
++FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
++ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
++OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++*****************************************************************/
++
++
++
++#include "sieve_interface.h"
++
++#include "bytecode.h"
++#include "script.h"
++
++#include "xmalloc.h"
++#include <sys/types.h> 
++#include <sys/stat.h>
++#include <fcntl.h> 
++#include <unistd.h> 
++#include <netinet/in.h>
++
++#include <string.h>
++
++#include "map.h"
++
++static void dump2(bytecode_input_t *d, int len);
++static int dump2_test(bytecode_input_t * d, int i);
++
++static int load(int fd, bytecode_input_t ** d)
++{  
++    const char * data=NULL;
++    struct stat sbuf;
++    unsigned long len=0;
++    
++    if (fstat(fd, &sbuf) == -1) {
++	printf("IOERROR: fstating sieve script: %m");
++	return SIEVE_FAIL;
++    }
++    
++    /*this reads in data and length from file*/
++    map_refresh(fd, 1, &(data), &len, sbuf.st_size,
++		"sievescript", "");
++    *d=(bytecode_input_t *)data;
++    
++    printf("\n");
++    
++    return (len/sizeof(int));
++}
++
++
++int main(int argc, char * argv[])
++{
++    bytecode_input_t * bc;
++    int script_fd;
++    
++    unsigned long len;
++    
++    if (argc!=2) {
++	 fprintf(stderr, "usage:\n %s script\n", argv[0]);
++	 exit(1);
++    }
++
++    /*get script*/
++    script_fd = open(argv[1], O_RDONLY);
++    if (script_fd == -1) 
++    {
++	printf("can not open script '%s'\n", argv[1]);
++	exit(1);
++    }
++    
++    lib_init();
++    len=load(script_fd,&bc);
++    close(script_fd);
++    
++    if (bc) {
++	dump2(bc, len );
++	exit(0);
++    } else {
++	exit(1);
++    }
++}
++
++static int write_list(int list_len, int i, bytecode_input_t * d)
++{
++    int x;
++    i++;
++    for (x=0; x<list_len; x++)
++    {
++	const char *data;
++	int len;
++	
++	i = unwrap_string(d, i, &data, &len);
++	
++	printf("{%d}%s\n", len, data);
++    }
++    return i;
++}
++
++static int printComparison(bytecode_input_t *d ,int i)
++{
++    printf("Comparison: ");
++    switch(ntohl(d[i].value))
++    {
++    case B_IS: printf("Is"); break;
++    case B_CONTAINS:printf("Contains"); break;
++    case B_MATCHES: printf("Matches"); break;
++    case B_REGEX: printf("Regex"); break;
++    case B_COUNT:
++	printf("Count");
++	
++	switch(ntohl(d[i+1].value))
++	{
++	case B_GT: printf(" greater than "); break;   
++	case B_GE: printf(" greater than or equal "); break;
++	case B_LT: printf(" less than "); break;
++	case B_LE: printf(" less than or equal "); break;
++	case B_NE: printf(" not equal "); break;
++	case B_EQ: printf(" equal "); break;
++	}
++
++	break;
++    case B_VALUE:
++	printf("Value");
++	
++	switch(ntohl(d[i+1].value))
++	{
++	case B_GT: printf(" greater than "); break;   
++	case B_GE: printf(" greater than or equal ");break;
++	case B_LT: printf(" less than ");    break;
++	case B_LE: printf(" less than or equal ");break;
++	case B_NE: printf(" not equal ");    break;
++	case B_EQ: printf(" equal ");break;
++	}
++	
++	break;
++    default:
++	exit(1);
++    }
++
++    switch (ntohl(d[i+2].value))
++    {
++    case B_ASCIICASEMAP: printf("   (ascii-casemap) "); break;
++    case B_OCTET: printf("    (octet) "); break;
++    case B_ASCIINUMERIC:  printf("   (ascii-numeric) "); break;
++    default: exit(1);
++    }
++    
++    printf("\n");
++    return i+3;
++}
++
++
++static int dump2_test(bytecode_input_t * d, int i)
++{
++    int l,x;
++    switch(ntohl(d[i].value)) {
++    case BC_FALSE:
++	printf("false");
++	i++;
++	break;
++    case BC_TRUE:
++	printf("true");
++	i++;
++	break;
++    case BC_NOT:/*2*/
++	/* XXX 
++	   there is a value being skipped in the second pass...
++	   no idea what it does, but it isn't carried to here...
++	   see bytecodee.c */
++	printf(" not(");
++	i=dump2_test(d, i+1);
++	printf(")\n");
++	break;
++    case BC_EXISTS:
++	printf("exists");
++	i=write_list(ntohl(d[i+1].len), i+2, d);
++	break;
++    case BC_SIZE:
++	printf("size");
++	if (ntohl(d[i+1].value)==B_OVER) {
++	    /* over */
++	    printf("over %d", ntohl(d[i+2].value));
++	} else {
++	    /* under */
++	    printf("under %d", ntohl(d[i+2].value));
++	}
++	i+=3;
++	break;
++    case BC_ANYOF:/*5*/
++	printf("any of \n(");
++	l=ntohl(d[i+1].len);
++	i+=3;
++	
++	for (x=0; x<l; x++)
++	{
++	    i=dump2_test(d,i);
++	    if((x+1)<l)
++		printf(" OR ");
++	}
++	
++	printf(")\n");	 
++	break;
++    case BC_ALLOF:/*6*/
++	printf("all of \n(");
++	l=ntohl(d[i+1].len);
++	i+=3;
++	
++	for (x=0; x<l; x++)
++	{
++	    i=dump2_test(d,i);
++	    if((x+1)<l)
++		printf(" AND ");
++	}
++	
++	printf(")\n");
++	break;
++    case BC_ADDRESS:/*7*/
++	printf("Address (");
++	i=printComparison(d, i+1);
++	printf("               type: ");
++	switch(ntohl(d[i++].value))
++	{
++	case B_ALL: printf("all"); break;
++	case B_LOCALPART:printf("localpart"); break;
++	case B_DOMAIN:printf("domain"); break;
++	case B_USER:printf("user"); break;
++	case B_DETAIL:printf("detail"); break;
++	}
++	printf("              Headers:");
++	i=write_list(ntohl(d[i].len), i+1, d);
++	printf("              Data:");
++	i=write_list(ntohl(d[i].len), i+1, d);
++	printf("             ]\n");
++	break;
++    case BC_ENVELOPE:/*8*/
++	printf("Envelope (");
++	i=printComparison(d, i+1);
++	printf("                type: ");
++	switch(ntohl(d[i++].value))
++	{
++	case B_ALL: printf("all"); break;
++	case B_LOCALPART:printf("localpart"); break;
++	case B_DOMAIN:printf("domain"); break;
++	case B_USER:printf("user"); break;
++	case B_DETAIL:printf("detail"); break;
++	}
++	printf("              Headers:");
++	i=write_list(ntohl(d[i].len), i+1, d);
++	printf("              Data:");
++	i=write_list(ntohl(d[i].len), i+1, d);
++	printf("             ]\n");
++	break;
++    case BC_HEADER:/*9*/
++	printf("Header [");
++	i= printComparison(d, i+1);
++	printf("              Headers: ");
++	i=write_list(ntohl(d[i].len), i+1, d);
++	printf("              Data: ");
++	i=write_list(ntohl(d[i].len), i+1, d);
++	printf("             ]\n");
++	break;
++    default:
++	printf("WERT %d ", ntohl(d[i].value));
++    }   
++    return i;
++}
++
++static void dump2(bytecode_input_t *d, int bc_len)
++{
++    int i;
++    const char *data;
++    int len;
++    
++    if(memcmp(d, BYTECODE_MAGIC, BYTECODE_MAGIC_LEN)) {
++	printf("not a bytecode file [magic number test failed]\n");
++	return;
++    }
++
++    i = BYTECODE_MAGIC_LEN / sizeof(bytecode_input_t);
++
++    printf("Sievecode version %d\n", ntohl(d[i].op));
++    if(!d) return;
++    
++    for(i++; i<bc_len;) 
++    {
++	switch(ntohl(d[i].op)) {
++	    
++	case B_STOP:/*0*/
++	    printf("%d: STOP\n",i);
++	    i++;
++	    break;
++	    
++	case B_KEEP:/*1*/
++	    printf("%d: KEEP\n",i);
++	    i++;
++	    break;
++	    
++	case B_DISCARD:/*2*/
++	    printf("%d: DISCARD\n",i);
++	    i++;
++	    break;
++	    
++	case B_REJECT:/*3*/
++	    i = unwrap_string(d, i+1, &data, &len);
++	    printf("%d: REJECT {%d}%s\n", i, len, data);
++	    break;
++
++	case B_FILEINTO: /*4*/
++	    i = unwrap_string(d, i+1, &data, &len);
++	    printf("%d: FILEINTO {%d}%s\n",i, len, data);
++	    break;
++
++	case B_REDIRECT: /*5*/
++	    i = unwrap_string(d, i+1, &data, &len);
++	    printf("%d: REDIRECT {%d}%s\n",i,len,data);
++	    break;
++	     
++	case B_IF:/*6*/
++	    printf("%d: IF (ends at %d)",i, ntohl(d[i+1].value));
++
++            /* there is no short circuiting involved here*/
++	    i = dump2_test(d,i+2);
++	    printf("\n");
++
++	    break;
++
++	case B_MARK:/*7*/
++	    printf("%d: MARK\n",i);
++	    i++;
++	    break;
++
++	case B_UNMARK:/*8*/
++	    printf("%d: UNMARK\n",i);
++	    i++;
++	    break;
++
++	case B_ADDFLAG: /*9*/
++	    printf("%d: ADDFLAG  {%d}\n",i,ntohl(d[i+1].len));
++	    i=write_list(ntohl(d[i+1].len),i+2,d);
++	    break;
++
++	case B_SETFLAG: /*10*/
++	    printf("%d: SETFLAG  {%d}\n",i,ntohl(d[i+1].len));
++	    i=write_list(ntohl(d[i+1].len),i+2,d);
++	    break;
++	    
++	case B_REMOVEFLAG: /*11*/
++	    printf("%d: REMOVEFLAG  {%d}\n",i,ntohl(d[i+1].len));
++	    i=write_list(ntohl(d[i+1].len),i+2,d);
++	    break;
++	    
++	case B_DENOTIFY:/*12*/
++	    printf("%d: DENOTIFY\n",i);
++	    i++; 
++	    printf("            PRIORITY(%d) Comparison type %d (relat %d)\n",
++		   ntohl(d[i].value), ntohl(d[i+1].value), ntohl(d[i+2].value));
++	    i+=3;
++
++	    i = unwrap_string(d, i+1, &data, &len);
++	    
++	    printf("           ({%d}%s)\n", len, (!data ? "[nil]" : data));
++	    break;
++	    
++	case B_NOTIFY: /*13*/
++	    i = unwrap_string(d, i+1, &data, &len);
++
++	    printf("%d: NOTIFY METHOD({%d}%s)\n",i,len,data);
++
++	    i = unwrap_string(d, i, &data, &len);
++
++	    printf("            ID({%d}%s) OPTIONS ", len,
++		   (!data ? "[nil]" : data));
++
++	    i=write_list(ntohl(d[i].len),i+1,d);
++	    
++	    printf("            PRIORITY(%d)\n", ntohl(d[i].value));
++      	    i++;
++		  
++	    i = unwrap_string(d, i, &data, &len);
++
++	    printf("            MESSAGE({%d}%s)\n", len, data);
++
++	    break;
++
++	case B_VACATION:/*14*/
++	    printf("%d: VACATION\n",i);
++	    /*add address list here!*/
++	    i=write_list(ntohl(d[i+1].len),i+2,d);
++
++	    i = unwrap_string(d, i, &data, &len);
++	  
++	    printf("%d SUBJ({%d}%s) \n",i, len, (!data ? "[nil]" : data));
++	    
++	    i = unwrap_string(d, i, &data, &len);
++
++	    printf("%d MESG({%d}%s) \n", i, len, (!data ? "[nil]" : data));
++
++	    printf("DAYS(%d) MIME(%d)\n", ntohl(d[i].value), ntohl(d[i+1].value));
++	    i+=2;
++
++	    break;
++	case B_NULL:/*15*/
++	    printf("%d:NULL\n",i);
++	    i++;
++	    break;
++	case B_JUMP:/*16*/
++	    printf("%d:JUMP %d\n",i, ntohl(d[i+1].jump));
++	    i+=2;
++	    break;		  
++	default:
++	    printf("%d: %d (NOT AN OP)\n",i,ntohl(d[i].op));
++	    exit(1);
++	}
++    }
++    printf("full len is: %d\n", bc_len);
++}
++
++
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/tree.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/tree.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,224 @@
++/* tree.c -- abstract syntax tree handling
++ * Larry Greenfield
++ * $Id$
++ */
++/***********************************************************
++        Copyright 1999 by Carnegie Mellon University
++
++                      All Rights Reserved
++
++Permission to use, copy, modify, and distribute this software and its
++documentation for any purpose and without fee is hereby granted,
++provided that the above copyright notice appear in all copies and that
++both that copyright notice and this permission notice appear in
++supporting documentation, and that the name of Carnegie Mellon
++University not be used in advertising or publicity pertaining to
++distribution of the software without specific, written prior
++permission.
++
++CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
++FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
++ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
++OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++******************************************************************/
++
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include <stdlib.h>
++#include "xmalloc.h"
++
++#include "tree.h"
++#include "sieve.h"
++
++stringlist_t *new_sl(char *s, stringlist_t *n)
++{
++    stringlist_t *p = (stringlist_t *) xmalloc(sizeof(stringlist_t));
++    p->s = s;
++    p->next = n;
++    return p;
++}
++
++
++tag_t *new_tag(int type, char *s)
++{
++    tag_t *p = (tag_t *) xmalloc(sizeof(tag_t));
++    p->type = type;
++    p->arg = s;
++    return p;
++}
++
++taglist_t *new_taglist(tag_t *t, taglist_t *n)
++{
++    taglist_t *p = (taglist_t *) xmalloc(sizeof(taglist_t));
++    p->t = t;
++    p->next = n;
++    return p;
++}
++
++test_t *new_test(int type) 
++{
++    test_t *p = (test_t *) xmalloc(sizeof(test_t));
++    p->type = type;
++    return p;
++}
++
++testlist_t *new_testlist(test_t *t, testlist_t *n)
++{
++    testlist_t *p = (testlist_t *) xmalloc(sizeof(testlist_t));
++    p->t = t;
++    p->next = n;
++    return p;
++}
++
++commandlist_t *new_command(int type)
++{
++    commandlist_t *p = (commandlist_t *) xmalloc(sizeof(commandlist_t));
++    p->type = type;
++    p->next = NULL;
++    return p;
++}
++
++commandlist_t *new_if(test_t *t, commandlist_t *y, commandlist_t *n)
++{
++    commandlist_t *p = (commandlist_t *) xmalloc(sizeof(commandlist_t));
++    p->type = IF;
++    p->u.i.t = t;
++    p->u.i.do_then = y;
++    p->u.i.do_else = n;
++    p->next = NULL;
++    return p;
++}
++
++void free_sl(stringlist_t *sl) 
++{
++    stringlist_t *sl2;
++    
++    while (sl != NULL) {
++	sl2 = sl->next;
++
++	if (sl->s) free(sl->s);
++
++	free(sl);
++	sl = sl2;
++    }
++}
++
++
++void free_test(test_t *t);
++
++static void free_tl(testlist_t *tl)
++{
++    testlist_t *tl2;
++
++    while (tl) {
++	tl2 = tl->next;
++
++	if (tl->t) free_test(tl->t);
++
++	free(tl);
++	tl = tl2;
++    }
++}
++
++void free_test(test_t *t)
++{
++    if (t == NULL) return;
++
++    switch (t->type) {
++    case ANYOF:
++    case ALLOF:
++	free_tl(t->u.tl);
++	break;
++
++    case EXISTS:
++	free_sl(t->u.sl);
++	break;
++
++    case SIZE:
++    case SFALSE:
++    case STRUE:
++	break;
++
++    case HEADER:
++	free_sl(t->u.h.sl);
++	free_sl(t->u.h.pl);
++	
++	break;
++
++    case ADDRESS:
++	free_sl(t->u.ae.sl);
++	free_sl(t->u.ae.pl);
++	break;
++
++    case NOT:
++	free_test(t->u.t);
++	break;
++    }
++
++    free(t);
++}
++
++void free_tree(commandlist_t *cl)
++{
++    commandlist_t *cl2;
++
++    while (cl != NULL) {
++	cl2 = cl->next;
++	switch (cl->type) {
++	case IF:
++	    free_test(cl->u.i.t);
++	    free_tree(cl->u.i.do_then);
++	    free_tree(cl->u.i.do_else);
++	    break;
++
++	case FILEINTO:
++	case REDIRECT:
++	case REJCT:
++	    if (cl->u.str) free(cl->u.str);
++	    break;
++
++	case VACATION:
++	    if (cl->u.v.subject) free(cl->u.v.subject);
++	    if (cl->u.v.addresses) free_sl(cl->u.v.addresses);
++	    if (cl->u.v.message) free(cl->u.v.message);
++	    break;
++	    
++	case SETFLAG:
++	case ADDFLAG:
++	case REMOVEFLAG:
++	    free_sl(cl->u.sl);
++	    break;
++
++	case KEEP:
++	case STOP:
++	case DISCARD:
++	    break;
++
++	case NOTIFY:
++	    if (cl->u.n.method) free(cl->u.n.method);
++	    if (cl->u.n.id) free(cl->u.n.id);
++	    if (cl->u.n.options) free_sl(cl->u.n.options);
++	    if (cl->u.n.message) free(cl->u.n.message);
++	    break;
++
++	case DENOTIFY:
++	    if (cl->u.d.pattern) {
++#ifdef ENABLE_REGEX
++		if (cl->u.d.comptag == REGEX) {
++		    regfree((regex_t *) cl->u.d.pattern);
++		}
++#endif
++		free(cl->u.d.pattern);
++	    }
++	    break;
++	}
++
++	free(cl);
++	cl = cl2;
++    }
++}
+diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/tree.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/libsieve/tree.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,139 @@
++/* tree.h -- abstract syntax tree
++ * Larry Greenfield
++ * $Id$
++ */
++/***********************************************************
++        Copyright 1999 by Carnegie Mellon University
++
++                      All Rights Reserved
++
++Permission to use, copy, modify, and distribute this software and its
++documentation for any purpose and without fee is hereby granted,
++provided that the above copyright notice appear in all copies and that
++both that copyright notice and this permission notice appear in
++supporting documentation, and that the name of Carnegie Mellon
++University not be used in advertising or publicity pertaining to
++distribution of the software without specific, written prior
++permission.
++
++CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
++FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
++ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
++OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++******************************************************************/
++
++#ifndef TREE_H
++#define TREE_H
++
++#include "comparator.h"
++
++/* abstract syntax tree for sieve */
++typedef struct Stringlist stringlist_t;
++typedef struct Commandlist commandlist_t;
++typedef struct Test test_t;
++typedef struct Testlist testlist_t;
++typedef struct Tag tag_t;
++typedef struct Taglist taglist_t;
++
++struct Stringlist {
++    char *s;
++    stringlist_t *next;
++};
++
++ 
++struct Tag {
++    int type;
++    char *arg;
++};
++
++struct Taglist {
++    tag_t *t;
++    taglist_t *next;
++};
++
++struct Test {
++    int type;
++    union {
++	testlist_t *tl; /* anyof, allof */
++	stringlist_t *sl; /* exists */
++	struct { /* it's a header test */
++	    int comptag;
++	    char * comparator;
++	    int relation;
++	    void *comprock;
++	    stringlist_t *sl;
++	    stringlist_t *pl;
++	} h;
++	struct { /* it's an address or envelope test */
++	    int comptag;
++	    char * comparator;
++	    int relation; 
++	    void *comprock;
++	    stringlist_t *sl;
++	    stringlist_t *pl;
++            int addrpart;
++	} ae; 
++	test_t *t; /* not */
++	struct { /* size */
++	    int t; /* tag */
++	    int n; /* param */
++	} sz;
++    } u;
++};
++
++struct Testlist {
++    test_t *t;
++    testlist_t *next;
++};
++
++struct Commandlist {
++    int type;
++    union {
++        char *str;
++	stringlist_t *sl; /* the parameters */
++	struct { /* it's an if statement */
++	    test_t *t;
++	    commandlist_t *do_then;
++	    commandlist_t *do_else;
++	} i;
++	struct { /* it's a vacation action */
++	    char *subject;
++	    int days;
++	    stringlist_t *addresses;
++	    char *message;
++	    int mime;
++	} v;
++	struct { /* it's a notify action */
++	    char *method;
++	    char *id;
++	    stringlist_t *options;
++	    int priority;
++	    char *message;
++	} n;
++	struct { /* it's a denotify action */
++	    int comptag;
++	    int relation;
++	    void *comprock;
++	    void *pattern;
++	    int priority;
++	} d;
++    } u;
++    struct Commandlist *next;
++};
++
++stringlist_t *new_sl(char *s, stringlist_t *n);
++tag_t *new_tag(int type, char *s);
++taglist_t *new_taglist(tag_t *t, taglist_t *n);
++test_t *new_test(int type);
++testlist_t *new_testlist(test_t *t, testlist_t *n);
++commandlist_t *new_command(int type);
++commandlist_t *new_if(test_t *t, commandlist_t *y, commandlist_t *n);
++
++void free_sl(stringlist_t *sl);
++void free_test(test_t *t);
++void free_tree(commandlist_t *cl);
++
++#endif
+diff -r 894f003d9f5f src/lib-sieve/cmu/map.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/map.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,55 @@
++#include "lib.h"
++#include "map.h"
++
++#include <unistd.h>
++
++static ssize_t read_full_n(int fd, void *data, size_t size)
++{
++	ssize_t ret, all_ret = 0;
++
++	while (size > 0) {
++		ret = read(fd, data, size);
++		if (ret <= 0)
++			return ret;
++
++		data = PTR_OFFSET(data, ret);
++		all_ret += ret;
++		size -= ret;
++	}
++
++	return all_ret;
++}
++
++void map_refresh(int fd, int onceonly __attr_unused__, const char **base,
++		 unsigned long *len, unsigned long newlen,
++		 const char *name, const char *mboxname __attr_unused__)
++{
++	ssize_t ret;
++	void *p;
++
++	if (newlen == 0) {
++		/* the file is a broken zero-byte file */
++		*len = 0;
++		return;
++	}
++
++	*base = p = i_malloc(newlen);
++	*len = newlen;
++
++	ret = read_full_n(fd, p, newlen);
++	if (ret < 0) {
++		i_error("read_full_n(%s) failed: %m", name);
++		ret = 0;
++	}
++
++	*len = ret;
++}
++
++void map_free(const char **base, unsigned long *len __attr_unused__)
++{
++	char *x = (char *) *base;
++
++	i_free(x);
++	*base = NULL;
++}
++
+diff -r 894f003d9f5f src/lib-sieve/cmu/map.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/map.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,10 @@
++#ifndef __MAP_H
++#define __MAP_H
++
++extern void map_refresh(int fd, int onceonly, const char **base,
++			unsigned long *len, unsigned long newlen,
++			const char *name, const char *mboxname);
++
++extern void map_free(const char **base, unsigned long *len);
++
++#endif
+diff -r 894f003d9f5f src/lib-sieve/cmu/xmalloc.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/cmu/xmalloc.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,26 @@
++#ifndef __XMALLOC_H
++#define __XMALLOC_H
++
++#include <stdlib.h>
++#include <string.h>
++
++#define xmalloc(n) malloc(n)
++#define xrealloc(n, m) realloc(n, m)
++#define xzmalloc(n) calloc(n, 1)
++#define xstrdup(s) strdup(s)
++
++/* missing headers.. */
++#include <sys/types.h>
++#include <netinet/in.h>
++#include <regex.h>
++#include <fcntl.h>
++
++/* dovecot kludges */
++#include "lib.h"
++
++/* we don't have strlcpy, but strocpy is the same except for return value */
++#define strlcpy strocpy
++
++#define lcase str_lcase
++
++#endif
+diff -r 894f003d9f5f src/lib-sieve/sieve-implementation-private.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/sieve-implementation-private.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,62 @@
++#ifndef __SIEVE_IMPLEMENTATION_PRIVATE_H
++#define	__SIEVE_IMPLEMENTATION_PRIVATE_H
++
++#include "lib.h"
++#include "mail-storage.h"
++
++struct sieve_script;
++
++#define DUPLICATE_DEFAULT_KEEP (3600 * 24)
++
++struct sieve_implementation_vfuncs {
++	int (*compile) 
++		(struct sieve_script *script, bool verify_only);
++	int (*run) 
++		(struct sieve_script *script, void *context);
++	const char *(*get_capabilities) ();
++};
++
++struct sieve_implementation {
++	const char *name;
++	struct sieve_implementation_vfuncs v;
++};
++
++/* Error management */
++void sieve_clear_error(void);
++void sieve_set_error(const char *fmt, ...);
++void sieve_set_internal_error(void);
++void sieve_set_critical(const char *fmt, ...);
++
++void sieve_implementation_register(struct sieve_implementation *sieveimpl);
++
++void sieve_implementation_unregister(struct sieve_implementation *sieveimpl);
++
++void sieve_register_implementations(void);
++
++const char *const *sieve_runenv_get_mail_headers
++	(void *context, const char *field);
++const char *sieve_runenv_get_mail_first_header
++	(void *context, const char *field);
++uoff_t sieve_runenv_get_mail_size
++	(void *context);
++int sieve_runenv_get_envelope
++  (void *context, const char *field, array_t *contents);
++
++int sieve_runenv_is_duplicate
++	(void *context, const void *id, size_t id_size);
++void sieve_runenv_mark_duplicate
++  (void *context, const void *id, size_t id_size, time_t interval);
++
++int sieve_runenv_send_message
++  (void *context, const char *from, const char *to, const char *subject,
++   bool mime, const char *msg, const char **outmsgid);
++int sieve_runenv_send_rejection
++  (void *context, const char *reason);
++int sieve_runenv_send_forward
++	(void *context, const char *forwardto);
++int sieve_runenv_mail_save
++  (void *context, const char *mailbox, char **flags, unsigned int nflags);
++
++#include "sieve-implementation.h"
++
++#endif
+diff -r 894f003d9f5f src/lib-sieve/sieve-implementation.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/sieve-implementation.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,235 @@
++#include "lib.h"
++#include "array.h"
++#include "ioloop.h"
++#include "sieve-implementation-private.h"
++#include "sieve-implementation.h"
++
++#include <time.h>
++
++/* Message to show to users when critical error occurs */
++#define CRITICAL_MSG \
++  "Internal error occurred. Refer to server log for more information."
++#define CRITICAL_MSG_STAMP CRITICAL_MSG " [%Y-%m-%d %H:%M:%S]"
++
++static array_t ARRAY_DEFINE(implementations, struct sieve_implementation *);
++
++static struct sieve_implementation *cursieveimpl = NULL;
++static struct sieve_runtime_environment *runenv = NULL;
++static char *sieve_error;
++
++void sieve_deinit(void)
++{
++	if (array_is_created(&implementations))
++		array_free(&implementations);
++}
++
++void sieve_implementation_register(struct sieve_implementation *sieveimpl)
++{
++	/* append it after the list, so the autodetection order is correct */
++	array_append(&implementations, &sieveimpl, 1);
++}
++
++void sieve_implementation_unregister(struct sieve_implementation *sieveimpl)
++{
++	struct sieve_implementation *const *impls;
++	unsigned int i, count;
++
++	impls = array_get(&implementations, &count);
++	for (i = 0; i < count; i++) {
++	 	if (impls[i] == sieveimpl) {
++			array_delete(&implementations, i, 1);
++			break;
++		}
++	}
++}
++
++/* FIXME: Make this function dependent on ./configure
++ */
++extern struct sieve_implementation cmu_sieve;
++void sieve_register_implementations()
++{
++	sieve_implementation_register(&cmu_sieve);
++}
++
++void sieve_set_runtime_environment(struct sieve_runtime_environment *env) 
++{
++	runenv = env;
++}
++
++void sieve_init(void)
++{
++	ARRAY_CREATE(&implementations, default_pool, struct sieve_implementation *, 8);
++	
++	sieve_register_implementations();
++}
++
++/* Sets the active implementation */
++int sieve_set_implementation(const char *name)
++{
++	struct sieve_implementation *const *impls;
++	unsigned int i, count;
++
++	i_assert(name != NULL);
++
++	impls = array_get(&implementations, &count);
++	for (i = 0; i < count; i++) {
++		if (strcasecmp(impls[i]->name, name) == 0) {
++			cursieveimpl = impls[i];
++			return 0;
++		}
++	}
++
++	return -1;
++}			
++
++void sieve_clear_error(void)
++{
++	i_free(sieve_error);
++	sieve_error = NULL;
++}
++
++void sieve_set_error(const char *fmt, ...)
++{
++	va_list va;
++
++	sieve_clear_error();
++
++	if (fmt != NULL) {
++		va_start(va, fmt);
++		sieve_error = i_strdup_vprintf(fmt, va);
++		va_end(va);
++	}
++}
++
++void sieve_set_internal_error(void)
++{
++	struct tm *tm;
++	char str[256];
++	
++	tm = localtime(&ioloop_time);
++	
++	i_free(sieve_error);
++	sieve_error =
++	  strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ?
++	  i_strdup(str) : i_strdup(CRITICAL_MSG);
++}
++
++void sieve_set_critical(const char *fmt, ...)
++{
++  	va_list va;
++  
++	sieve_clear_error();
++	if (fmt != NULL) {
++	  	va_start(va, fmt);
++		i_error("%s", t_strdup_vprintf(fmt, va));
++		va_end(va);
++		
++		/* critical errors may contain sensitive data, so let user
++		   see only "Internal error" with a timestamp to make it
++		   easier to look from log files the actual error message. */
++		sieve_set_internal_error();
++	}
++}
++
++const char *sieve_get_last_error(void)
++{
++  	/* We get here only in error situations, so we have to return some
++	   error. If storage->error is NULL, it means we forgot to set it at
++	   some point.. */
++  	return sieve_error != NULL ? sieve_error : "Unknown error";
++}
++
++int sieve_compile(struct sieve_script *script, bool verify_only) 
++{
++  	i_assert(cursieveimpl != NULL);
++	return cursieveimpl->v.compile(script, verify_only);
++}
++
++int sieve_run(struct sieve_script *script, void *context)
++{
++  	i_assert(cursieveimpl != NULL);
++	return cursieveimpl->v.run(script, context);
++}
++
++const char *sieve_get_capabilities(void)
++{
++  	i_assert(cursieveimpl != NULL);
++	return cursieveimpl->v.get_capabilities();
++}
++
++/* Runtime environment */
++
++const char *const *sieve_runenv_get_mail_headers
++  (void *context, const char *field)
++{
++	i_assert(runenv != NULL);
++	return runenv->get_mail_headers(context, field);
++}
++
++const char *sieve_runenv_get_mail_first_header(void *context, const char *field)
++{
++	const char *const *list = sieve_runenv_get_mail_headers(context, field);
++	return list == NULL ? NULL : list[0];
++}
++
++uoff_t sieve_runenv_get_mail_size
++  (void *context)
++{
++  	i_assert(runenv != NULL);
++	return runenv->get_mail_size(context);
++}
++
++int sieve_runenv_is_duplicate
++  (void *context, const void *id, size_t id_size)
++{
++  	i_assert(runenv != NULL);
++	return runenv->is_duplicate(context, id, id_size);
++}
++
++void sieve_runenv_mark_duplicate
++  (void *context, const void *id, size_t id_size, time_t interval)
++{
++  	i_assert(runenv != NULL);
++	runenv->mark_duplicate(context, id, id_size, interval);
++}
++
++int sieve_runenv_send_message
++  (void *context, const char *from, const char *to, const char *subject,
++   bool mime, const char *msg, const char **outmsgid)
++{
++	i_assert(runenv != NULL);
++
++	return runenv->send_message
++	  (context, from, to, subject, mime, msg, outmsgid);
++}
++
++int sieve_runenv_get_envelope
++	(void *context, const char *field, array_t *contents)
++{
++  	i_assert(runenv != NULL);
++
++	return runenv->get_envelope(context, field, contents);
++}
++
++int sieve_runenv_send_rejection
++	(void *context, const char *reason)
++{
++  	i_assert(runenv != NULL);
++
++	return runenv->send_rejection(context, reason);
++}
++
++int sieve_runenv_send_forward(void *context, const char *forwardto)
++{
++  	i_assert(runenv != NULL);
++
++	return runenv->send_forward(context, forwardto);
++}
++
++int sieve_runenv_mail_save
++  (void *context, const char *mailbox, char **flags, unsigned int nflags)
++{
++  	i_assert(runenv != NULL);
++
++	return runenv->mail_save(context, mailbox, flags, nflags);
++}
+diff -r 894f003d9f5f src/lib-sieve/sieve-implementation.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sieve/sieve-implementation.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,46 @@
++#ifndef __SIEVE_IMPLEMENTATION_H
++#define __SIEVE_IMPLEMENTATION_H
++
++struct sieve_script;
++
++struct sieve_runtime_environment {
++	const char *const *(*get_mail_headers)
++		(void *context, const char *field);
++	uoff_t (*get_mail_size)
++		(void *context);
++	int (*get_envelope)
++		(void *context, const char *field, array_t *contents);
++
++	int (*is_duplicate)
++		(void *context, const void *id, size_t id_size);
++	void (*mark_duplicate)
++		(void *context, const void *id, size_t id_size, time_t interval);
++
++	int (*send_message)
++		(void *context, const char *from, const char *to, const char *subject,
++		bool mime, const char *msg, const char **outmsgid);
++	int (*send_rejection)
++		(void *context, const char *reason);
++	int (*send_forward)
++		(void *context, const char *forwardto);
++	int (*mail_save)
++		(void *context, const char *mailbox, char **flags, unsigned int nflags);
++};
++
++void sieve_init(void);
++void sieve_deinit(void);
++
++int sieve_set_implementation(const char *name);
++
++void sieve_set_runtime_environment(struct sieve_runtime_environment *env);
++
++const char *sieve_get_last_error(void);
++
++int sieve_compile(struct sieve_script *script, bool verify_only);
++
++int sieve_run(struct sieve_script *script, void *context);
++
++const char *sieve_get_capabilities(void);
++
++#endif
++
+diff -r 894f003d9f5f src/lib-sievestorage/Makefile.am
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sievestorage/Makefile.am	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,19 @@
++noinst_LIBRARIES = libsievestorage.a
++
++INCLUDES = \
++	-I$(top_srcdir)/src/lib \
++	-I$(top_srcdir)/src/lib-mail \
++  -I$(dovecotsievedir)/src/libsieve
++
++libsievestorage_a_SOURCES = \
++	sieve-save.c \
++	sieve-script.c \
++	sieve-list.c \
++	sieve-storage.c 
++
++noinst_HEADERS = \
++	sieve-save.h \
++	sieve-script.h \
++	sieve-list.h \
++	sieve-storage.h \
++	sieve-storage-private.h 
+diff -r 894f003d9f5f src/lib-sievestorage/sieve-list.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sievestorage/sieve-list.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,116 @@
++#include "lib.h"
++#include "str.h"
++#include "sieve-storage-private.h"
++#include "sieve-script.h"
++#include "sieve-list.h"
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <sys/types.h>
++#include <dirent.h>
++#include <sys/stat.h>
++
++struct sieve_list_context {
++	pool_t pool;
++	struct sieve_storage *storage;
++
++	const char *active;
++	const char *dir;
++	DIR *dirp;
++
++	unsigned int seen_active:1; // Just present for assertions
++};
++
++struct sieve_list_context *sieve_storage_list_init
++	(struct sieve_storage *storage)
++{	
++	struct sieve_list_context *ctx;
++	const char *active;
++	pool_t pool;
++	DIR *dirp;
++
++	/* Open the directory */
++	if ( (dirp = opendir(storage->dir)) == NULL ) {
++		sieve_storage_set_critical(storage, "opendir(%s) failed: %m",
++					   storage->dir);
++		return NULL;
++	}
++
++	t_push();
++
++	/* Get the name of the active script */
++	if ( (active = sieve_storage_get_active_scriptname(storage)) 
++		== NULL ) {
++		t_pop();
++		return NULL;
++	}
++
++	pool = pool_alloconly_create("sieve_list_context", 4096);
++	ctx = p_new(pool, struct sieve_list_context, 1);
++	ctx->pool = pool;
++	ctx->storage = storage;
++	ctx->dirp = dirp;
++	ctx->active = p_strdup(pool, active);
++	ctx->seen_active = FALSE;
++
++	t_pop();
++
++	return ctx;
++}
++
++const char *sieve_storage_list_next
++	(struct sieve_list_context *ctx, bool *active)
++{
++	const struct sieve_storage *storage = ctx->storage;
++	struct dirent *dp;
++	const char *scriptname;
++
++	*active = FALSE;
++
++	for (;;) {
++		if ( (dp = readdir(ctx->dirp)) == NULL )
++			return NULL;
++
++		scriptname = sieve_storage_file_get_scriptname
++			(storage, dp->d_name);	
++		
++		if (scriptname != NULL ) {
++			/* Don't list our active sieve script link if the link 
++			 * resides in the script dir (generally a bad idea).
++			 */
++			if ( *(storage->link_path) == '\0' && 
++				strcmp(storage->active_fname, dp->d_name) == 0 )
++				continue;
++		
++			break;
++		}
++	}
++
++	if ( ctx->active != NULL && 
++		strcmp(scriptname, ctx->active) == 0 ) {
++		*active = TRUE;
++		ctx->active = NULL;
++	}
++
++	return scriptname;
++}
++
++int sieve_storage_list_deinit(struct sieve_list_context **ctx)
++{
++	if (closedir((*ctx)->dirp) < 0) {
++		sieve_storage_set_critical((*ctx)->storage, "closedir(%s) failed: %m",
++					   (*ctx)->storage->dir);
++
++		pool_unref((*ctx)->pool);
++		*ctx = NULL;
++		return -1;
++	}
++
++	pool_unref((*ctx)->pool);
++	*ctx = NULL;
++	return 1;
++}
++
++
++	
++    
+diff -r 894f003d9f5f src/lib-sievestorage/sieve-list.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sievestorage/sieve-list.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,30 @@
++#ifndef __SIEVE_LIST_H
++#define __SIEVE_LIST_H
++
++#include "lib.h"
++#include "str.h"
++#include "sieve-storage.h"
++#include "sieve-list.h"
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <dirent.h>
++#include <sys/stat.h>
++
++struct sieve_list_context;
++
++/* Create a context for listing the scripts in the storage */
++struct sieve_list_context *sieve_storage_list_init
++	(struct sieve_storage *storage);
++
++/* Get the next script in the storage. */
++const char *sieve_storage_list_next(struct sieve_list_context *ctx, bool *active);
++
++/* Destroy the listing context */
++int sieve_storage_list_deinit(struct sieve_list_context **ctx);
++
++#endif
++
++
++	
++    
+diff -r 894f003d9f5f src/lib-sievestorage/sieve-save.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sievestorage/sieve-save.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,322 @@
++#include "lib.h"
++#include "hostpid.h"
++#include "ioloop.h"
++#include "array.h"
++#include "buffer.h"
++#include "ostream.h"
++#include "ostream-crlf.h"
++#include "str.h"
++#include "sieve-storage-private.h"
++#include "sieve-save.h"
++#include "sieve-script.h"
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <utime.h>
++#include <sys/stat.h>
++
++struct sieve_save_context {
++	pool_t pool;
++
++	struct sieve_storage *storage;
++	const char *scriptname;
++	struct sieve_script *scriptobject;
++
++	struct istream *input;
++	struct ostream *output;
++	int fd;
++	const char *tmp_path;
++
++	unsigned int failed:1;
++	unsigned int moving:1;
++	unsigned int finished:1;
++};
++
++static const char *sieve_generate_tmp_filename(const char *scriptname, const struct timeval *tv)
++{
++	static unsigned int create_count = 0;
++	static time_t first_stamp = 0;
++
++	if (first_stamp == 0 || first_stamp == ioloop_time) {
++	  	/* it's possible that within last second another process had
++		   the same PID as us. Use usecs to make sure we don't create
++		   duplicate base name. */
++		first_stamp = ioloop_time;
++		return t_strdup_printf
++		  ("%s-%s.P%sQ%uM%s.%s",
++		   scriptname,
++		   dec2str(tv->tv_sec), my_pid,
++		   create_count++,
++		   dec2str(tv->tv_usec), my_hostname);
++	} else {
++		/* Don't bother with usecs. Saves a bit space :) */
++		return t_strdup_printf
++		  ("%s-%s.P%sQ%u.%s",
++		   scriptname,
++		   dec2str(tv->tv_sec), my_pid,
++		   create_count++, my_hostname);
++	}
++}
++
++static int sieve_create_tmp
++(struct sieve_storage *storage, const char *scriptname, const char **fpath_r)
++{
++ 	const char *path, *tmp_fname;
++	struct stat st;
++	struct timeval *tv, tv_now;
++	pool_t pool;
++	int fd;
++
++	tv = &ioloop_timeval;
++	pool = pool_alloconly_create("script_tmp", 4096);
++	for (;;) {
++		p_clear(pool);
++		tmp_fname = sieve_generate_tmp_filename(scriptname, tv);
++
++		path = p_strconcat(pool, storage->dir, "/tmp/", tmp_fname, ".sieve", NULL);
++		if ( stat(path, &st) < 0 ) {
++			if ( errno == ENOENT) {
++				/* NICE! doesn't exist */
++				fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
++		  
++				/* Something went wrong */
++				if (fd != -1 || errno != EEXIST)
++			  		break;
++			} else {
++				sieve_storage_set_critical(storage,
++                	"stat(%s) failed: %m", path);
++				return -1;
++			}
++		}
++
++		/* Wait and try again - very unlikely */
++		sleep(2);
++		tv = &tv_now;
++		if (gettimeofday(&tv_now, NULL) < 0)
++			i_fatal("gettimeofday(): %m");
++	}
++	
++	*fpath_r = t_strdup(path);
++	if (fd == -1) {
++		if (ENOSPACE(errno)) {
++			sieve_storage_set_error(storage,
++				"Not enough disk space");
++		} else {
++			sieve_storage_set_critical(storage,
++				"open(%s) failed: %m", path);
++        }
++    } 
++
++	pool_unref(pool);
++	return fd;
++}
++
++static int sieve_script_move(struct sieve_save_context *ctx,
++  const char *dst)
++{
++	int failed;
++
++	t_push();
++
++	/* Using rename() to ensure existing files are replaced
++	 * without conflicts with other processes using the same
++	 * file. The kernel wont fully delete the original until
++	 * all processes have closed the file.
++	 */
++	if (rename(ctx->tmp_path, dst) == 0)
++		failed = FALSE;
++	else {
++		failed = TRUE;
++		if (ENOSPACE(errno)) {
++			sieve_storage_set_error
++			  (ctx->storage, "Not enough disk space");
++		} else {
++			sieve_storage_set_critical
++			  (ctx->storage, "link(%s, %s) failed: %m", ctx->tmp_path, dst);
++		}
++	}
++
++	/* Always destroy temp file */
++	(void)unlink(ctx->tmp_path);
++
++	t_pop();
++	return !failed;
++}
++
++struct sieve_save_context *
++sieve_storage_save_init(struct sieve_storage *storage,
++	const char *scriptname, struct istream *input)
++{
++	struct sieve_save_context *ctx;
++	pool_t pool;
++	struct ostream *output;
++	const char *path;
++
++	/* Prevent overwriting the active script link when it resides in the 
++	 * sieve storage directory.
++	 */
++	if ( *(storage->link_path) == '\0' ) {
++		const char *svext;
++		size_t namelen;
++
++		svext = strrchr(storage->active_fname, '.');
++		namelen = svext - storage->active_fname;
++		if ( svext != NULL && strncmp(svext+1, "sieve", 5) == 0 &&
++			strlen(scriptname) == namelen && 
++			strncmp(scriptname, storage->active_fname, namelen) == 0 ) 
++		{
++			sieve_storage_set_error(
++				storage, "Script name '%s' is reserved for internal use.", 
++				scriptname); 
++			return NULL;
++		}
++	}
++
++	pool = pool_alloconly_create("sieve_save_context", 4096);
++	ctx = p_new(pool, struct sieve_save_context, 1);
++	ctx->pool = pool;
++	ctx->storage = storage;
++	ctx->scriptname = scriptname;
++	ctx->scriptobject = NULL;
++
++	t_push();
++
++	ctx->fd = sieve_create_tmp(storage, scriptname, &path);
++	if (ctx->fd == -1) {
++		ctx->failed = TRUE;
++		t_pop();
++		pool_unref(pool);
++		return NULL;
++	}
++
++	ctx->input = input;
++
++	output = o_stream_create_file(ctx->fd, system_pool, 0, FALSE);
++	/*ctx->output = (storage->flags & SIEVE_STORAGE_FLAG_SAVE_CRLF) != 0 ?
++		o_stream_create_crlf(default_pool, output) :
++		o_stream_create_lf(default_pool, output);
++	o_stream_unref(&output);*/
++	ctx->output = output;
++
++	ctx->tmp_path = p_strdup(pool, path);
++	ctx->failed = FALSE;
++
++	t_pop();	
++	return ctx;
++}
++
++int sieve_storage_save_continue(struct sieve_save_context *ctx)
++{
++	if (o_stream_send_istream(ctx->output, ctx->input) < 0) {
++		sieve_storage_set_critical(ctx->storage,
++			"o_stream_send_istream(%s) failed: %m", ctx->tmp_path);
++		ctx->failed = TRUE;
++		return -1;
++	}
++	return 0;
++}
++
++int sieve_storage_save_finish(struct sieve_save_context *ctx)
++{
++	int output_errno;
++
++	ctx->finished = TRUE;
++	if (ctx->failed && ctx->fd == -1) {
++		/* tmp file creation failed */
++		return -1;
++	}
++
++	t_push();
++	output_errno = ctx->output->stream_errno;
++	o_stream_destroy(&ctx->output);
++
++	if (fsync(ctx->fd) < 0) {
++		sieve_storage_set_critical(ctx->storage,
++					  "fsync(%s) failed: %m", ctx->tmp_path);
++		ctx->failed = TRUE;
++	}
++	if (close(ctx->fd) < 0) {
++		sieve_storage_set_critical(ctx->storage,
++					  "close(%s) failed: %m", ctx->tmp_path);
++		ctx->failed = TRUE;
++	}
++	ctx->fd = -1;
++
++	if (ctx->failed) {
++		/* delete the tmp file */
++		if (unlink(ctx->tmp_path) < 0 && errno != ENOENT) 
++			i_warning("Unlink(%s) failed: %m", ctx->tmp_path);
++
++		errno = output_errno;
++		if (ENOSPACE(errno)) {
++			sieve_storage_set_error(ctx->storage,
++					       "Not enough disk space");
++		} else if (errno != 0) {
++			sieve_storage_set_critical(ctx->storage,
++				"write(%s) failed: %m", ctx->tmp_path);
++		}
++
++		t_pop();
++		return -1;
++	}
++	t_pop();
++
++	return 0;
++}
++
++static void sieve_storage_save_destroy(struct sieve_save_context *ctx)
++{
++	if (ctx->scriptobject != NULL)
++		sieve_script_unref(&(ctx->scriptobject));
++
++	pool_unref(ctx->pool);
++}
++
++struct sieve_script *sieve_storage_save_get_tempscript
++	(struct sieve_save_context *ctx)
++{
++	if (ctx->failed) 
++		return NULL;
++
++	ctx->scriptobject = sieve_script_init_from_file(ctx->storage, ctx->scriptname,
++  	ctx->tmp_path, NULL);	
++
++	return ctx->scriptobject;
++}
++
++int sieve_storage_save_commit(struct sieve_save_context *ctx)
++{
++	const char *dest_path;
++	bool failed = FALSE;
++
++	i_assert(ctx->output == NULL);
++	i_assert(ctx->finished);
++
++	t_push();
++
++	dest_path = t_strconcat(ctx->storage->dir, "/", ctx->scriptname, ".sieve", NULL);
++
++	failed = !sieve_script_move(ctx, dest_path);
++	
++	t_pop();
++
++	sieve_storage_save_destroy(ctx);
++
++	return !failed;
++}
++
++void sieve_storage_save_abort(struct sieve_save_context *ctx)
++{
++	ctx->failed = TRUE;
++
++	if (!ctx->finished) 
++		(void)sieve_storage_save_finish(ctx);
++	else
++		(void)unlink(ctx->tmp_path);
++
++	i_assert(ctx->output == NULL);
++
++	sieve_storage_save_destroy(ctx);
++}
+diff -r 894f003d9f5f src/lib-sievestorage/sieve-save.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sievestorage/sieve-save.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,24 @@
++#ifndef __SIEVE_SAVE_H
++#define __SIEVE_SAVE_H
++
++#include "sieve-storage.h"
++
++struct sieve_save_context;
++
++struct sieve_save_context *
++sieve_storage_save_init(struct sieve_storage *storage,
++	const char *scriptname, struct istream *input);
++
++int sieve_storage_save_continue(struct sieve_save_context *ctx);
++
++int sieve_storage_save_finish(struct sieve_save_context *ctx);
++
++struct sieve_script *sieve_storage_save_get_tempscript
++  (struct sieve_save_context *ctx);
++
++void sieve_storage_save_abort(struct sieve_save_context *ctx);
++
++int sieve_storage_save_commit(struct sieve_save_context *ctx);
++
++#endif
++
+diff -r 894f003d9f5f src/lib-sievestorage/sieve-script.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sievestorage/sieve-script.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,634 @@
++#include "lib.h"
++#include "mempool.h"
++#include "hostpid.h"
++#include "ioloop.h"
++#include "istream.h"
++#include "file-copy.h"
++
++#include "sieve-storage.h"
++#include "sieve-storage-private.h"
++#include "sieve-script.h"
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <sys/stat.h>
++#include <ctype.h>
++#include <time.h>
++#include <fcntl.h>
++
++struct sieve_script *sieve_script_init_from_file
++  (struct sieve_storage *storage, const char *scriptname, 
++	const char *filename, bool *exists)
++{
++ 	int ret;
++	struct stat st;
++	pool_t pool;
++	struct sieve_script *script = NULL;	
++
++	/* Prevent initializing the active script link as a script when it
++     * resides in the sieve storage directory.
++	 */
++	if ( *(storage->link_path) == '\0' ) {
++		const char *fname;
++
++		fname = strrchr(filename, '/');
++		if ( fname == NULL )
++			fname = filename;
++		else
++			fname++;
++
++		if ( strcmp(fname, storage->active_fname) == 0 ) {
++			if ( exists != NULL )
++				*exists = FALSE;
++			return NULL;
++		}
++	}
++
++	/* Perform existance check if required */
++	if (exists != NULL) {
++		if (*exists) {
++			*exists = FALSE;
++			ret = stat(filename, &st);
++			
++			if ( ret < 0 ) {
++			  	if ( errno == ENOENT ) {
++				  	return NULL;
++				}
++
++				sieve_storage_set_critical
++				  (storage,	 
++					"Performing stat() on sieve file '%s' failed: %m", filename);
++				return NULL;
++			}
++
++			if ( !S_ISREG(st.st_mode) ) {
++			  	sieve_storage_set_critical
++				  (storage, "Sieve file '%s' is not a regular file.", filename);
++				return NULL;
++			}
++	
++			/* Script found */
++			*exists = TRUE;
++		}
++	}
++
++	pool = pool_alloconly_create("sieve_script", 4096);	
++	script = p_new(pool, struct sieve_script, 1);
++	script->pool = pool;
++	script->refcount = 1;
++	script->name = p_strdup(pool, scriptname);
++	script->storage = storage;
++	script->size = st.st_size;
++
++	script->filename = p_strdup(pool, filename);
++	script->istream = NULL;	
++
++	return script;
++}
++
++struct sieve_script *sieve_script_init
++	(struct sieve_storage *storage, const char *scriptname, bool *exists)
++{	
++	struct sieve_script *script;
++	const char *filename;
++
++	t_push();
++
++	filename = t_strconcat
++	  ( storage->dir, "/", scriptname, ".sieve", NULL );
++
++	script = sieve_script_init_from_file(storage, scriptname, filename, exists);
++
++	t_pop();
++
++	return script;
++}
++
++void sieve_script_ref(struct sieve_script *script)
++{
++	script->refcount++;
++}
++
++void sieve_script_unref(struct sieve_script **script)
++{
++  	i_assert((*script)->refcount > 0);
++	
++	if (--(*script)->refcount != 0)
++	  	return;
++
++	if ((*script)->istream != NULL ) 
++	  	i_stream_unref(&(*script)->istream);
++
++	pool_unref((*script)->pool);
++	
++	*script = NULL;
++}
++
++const char *sieve_script_name(struct sieve_script *script)
++{
++	return script->name;
++}
++
++/* sieve_script_filename(): 
++ * Returns the filename of the script. If the implemented storage
++ * does not actually store scripts as a file a copy must be put
++ * somewhere in /tmp or so. Don't use this function if you can
++ * also work with a stream.
++ */
++const char *sieve_script_filename(struct sieve_script *script)
++{
++ 	return script->filename;
++}
++
++static int
++sieve_storage_open_fd(struct sieve_storage *storage, const char *path, int *fd)
++{
++	*fd = open(path, O_RDONLY);
++	if (*fd != -1)
++		return 1;
++	if (errno == ENOENT)
++		return 0;
++
++	sieve_storage_set_critical(storage,
++				   "open(%s) failed: %m", path);
++	return -1;
++}
++
++int sieve_script_get_size(struct sieve_script *script, uoff_t *size)
++{
++	int ret = 0;
++
++	*size = 0;
++
++	if (script->size == 0) {
++		struct stat st;
++
++		ret = stat(script->filename, &st);
++
++		if ( ret < 0 ) {
++			if ( errno == ENOENT ) {
++				return 0;
++			}
++			
++			sieve_storage_set_critical
++			  (script->storage,
++			   "Performing stat() on sieve file '%s' failed: %m", script->filename);
++			
++			return -1;
++		}
++
++		script->size = st.st_size;
++	} 
++	
++	*size = script->size;
++	
++	return 1;
++}
++
++struct istream *
++sieve_script_open(struct sieve_script *script, bool *deleted_r)
++{
++	const struct stat *statbuf;
++	int ret = 0, fd = -1;
++
++	if ( deleted_r != NULL )
++		*deleted_r = FALSE;
++
++	if (script->istream == NULL) {
++		if ( (ret=sieve_storage_open_fd
++		    (script->storage, script->filename, &fd)) < 0) 
++			return NULL;
++
++		if (ret == 0) {
++			if ( deleted_r != NULL )
++				*deleted_r = TRUE;
++			return NULL;
++		}
++
++		script->istream = 
++		  i_stream_create_file(fd, default_pool,
++				       SIEVE_READ_BLOCK_SIZE, TRUE);
++
++		if ( script->istream != NULL ) {
++			statbuf = i_stream_stat(script->istream, 0);
++			script->size = statbuf->st_size;
++		}	
++	}
++
++	return script->istream;
++}
++
++const char *sieve_storage_file_get_scriptname
++	(const struct sieve_storage *storage __attr_unused__, const char *filename)
++{
++	const char *ext;
++
++	ext = strrchr(filename, '.');
++
++	if ( ext == NULL || ext == filename || strncmp(ext,".sieve",6) != 0 ) 
++		return NULL;
++	
++	return t_strdup_until(filename, ext);
++}
++
++static const char *sieve_storage_read_active_link
++	(struct sieve_storage *storage, bool *not_link)
++{
++  char linkbuf[PATH_MAX];
++  int ret;
++
++	if ( not_link != NULL )
++		*not_link = FALSE;
++
++	ret = readlink(storage->active_path, linkbuf, sizeof(linkbuf));
++
++	if ( ret < 0 ) {
++		if (errno == EINVAL) {
++			/* Our symlink is no symlink. Report 'no active script'.
++			 * Activating a script will automatically resolve this, so
++			 * there is no need to panic on this one.
++			 */
++			i_warning
++			  ("Active sieve script symlink %s is no symlink.",
++			   storage->active_path);
++			if ( not_link != NULL )
++				*not_link = TRUE;
++			return "";
++		}
++
++		if (errno != ENOENT ) {
++			/* We do need to panic otherwise */
++			sieve_storage_set_critical
++			  (storage,
++			  	"Performing readlink() on active sieve symlink '%s' failed: %m", 
++					storage->active_path);
++			return NULL;
++		}
++
++		return "";
++	}
++
++	/* ret is now assured to be valid, i.e. > 0 */
++	return t_strndup(linkbuf, ret);
++}
++
++static const char *sieve_storage_parse_link
++	(struct sieve_storage *storage, const char *link)
++{
++	const char *fname, *scriptname, *scriptpath;
++
++	/* Split link into path and filename */
++	fname = strrchr(link, '/');
++	if ( fname != NULL ) {
++		scriptpath = t_strdup_until(link, fname+1);
++		fname++;
++	} else {
++		scriptpath = "";
++		fname = link;
++	}
++
++	/* Check the script name */
++	scriptname = sieve_storage_file_get_scriptname(storage, fname);
++
++	/* Warn if link is deemed to be invalid */
++	if ( scriptname == NULL ) {
++		i_warning
++			("Active sieve script symlink %s is broken: "
++				"invalid scriptname (points to %s).",
++				storage->active_path, link);
++		return NULL;
++	}
++
++	/* Check whether the path is any good */
++	if ( strcmp(scriptpath, storage->link_path) != 0 &&
++		strcmp(scriptpath, storage->dir) != 0 ) {
++		i_warning
++			("Active sieve script symlink %s is broken: "
++				"invalid/unknown path to storage (points to %s).",
++				storage->active_path, link);
++		return NULL; 
++	}
++
++	return scriptname;
++}
++
++const char *sieve_storage_get_active_scriptname
++	(struct sieve_storage *storage)
++{
++	const char *link, *scriptname;
++
++	/* Read the active link */
++	link = sieve_storage_read_active_link(storage, NULL);
++
++	if ( link == NULL || *link == '\0' ) 
++		return link;
++
++	/* Parse the link */
++	scriptname = sieve_storage_parse_link(storage, link);
++
++	if (scriptname == NULL) {
++		/* Obviously someone has been playing with our symlink,
++		 * ignore this situation and report 'no active script'.
++		 * Activation should fix this situation.
++		 */
++		return "";
++	}
++
++	return scriptname;
++}
++
++struct sieve_script *
++  sieve_storage_get_active_script(struct sieve_storage *storage, bool *no_active)
++{
++	bool exists, no_link;
++	struct sieve_script *script;
++	const char *scriptname, *link;
++
++	*no_active = FALSE;
++
++	/* Read the active link */
++	link = sieve_storage_read_active_link(storage, &no_link);
++	
++	if ( link == NULL )
++		/* Error */
++		return NULL;
++
++	if ( *link == '\0' )
++	{
++		if (no_link) {
++			/* Try to open the active_path as a regular file */
++			return sieve_script_init_from_file
++				(storage, ".dovecot", storage->active_path, NULL);
++		}
++
++		*no_active = TRUE;
++		return NULL;
++	}
++
++	/* Parse the link */
++	scriptname = sieve_storage_parse_link(storage, link);
++
++	if (scriptname == NULL) {
++  		/* Obviously someone has been playing with our symlink,
++		 * ignore this situation and report 'no active script'.
++		 */
++		*no_active = TRUE;
++		return NULL;
++	}
++	
++	exists = TRUE;
++	script = sieve_script_init(storage, scriptname, &exists);	
++
++	if ( !exists ) {
++		i_warning
++		  ("Active sieve script symlink %s "
++		   "points to non-existent script (points to %s).",
++		   storage->active_path, link);
++	}
++	
++	*no_active = !exists;
++	return script;
++}
++
++int sieve_script_is_active(struct sieve_script *script)
++{
++	const char *aname;
++
++	t_push();
++	
++	aname = sieve_storage_get_active_scriptname(script->storage);
++	
++	if (aname == NULL) {
++		/* Critical error */
++		t_pop();
++		return -1;
++	}
++
++ 	/* Is the requested script active? */
++	if ( strcmp(script->name, aname) == 0 ) {
++		t_pop();
++		return 1;
++	}
++
++	t_pop();
++	return 0;
++}
++
++int sieve_script_delete(struct sieve_script **script) 
++{
++	struct sieve_storage *storage = (*script)->storage;
++	int ret = 0;
++
++
++	/* Is the requested script active? */
++	if ( sieve_script_is_active(*script) ) {
++		sieve_storage_set_error(storage, "Cannot delete the active sieve script.");
++		ret = -1;
++	} else {
++		ret = unlink((*script)->filename);
++
++		if ( ret < 0 ) {
++			if ( errno == ENOENT ) 
++				sieve_storage_set_error(storage, "Sieve script does not exist.");
++			else
++				sieve_storage_set_critical(
++					storage, "Performing unlink() failed on sieve file '%s': %m", 
++					(*script)->filename);
++		}	
++	}
++
++	/* Always deinitialize the script object */
++	sieve_script_unref(script);
++
++	return ret;	
++}
++
++static bool sieve_storage_rescue_regular_file(struct sieve_storage *storage)
++{
++	struct stat st;
++	
++	/* Stat the file */
++	if ( lstat(storage->active_path, &st) != 0 ) {
++		if ( errno != ENOENT ) {
++			sieve_storage_set_critical(storage, 
++				"Failed to stat active sieve script symlink (%s): %m.", 
++				storage->active_path); 
++			return FALSE;	
++		} 
++		return TRUE;
++	}
++
++  	if ( S_ISLNK( st.st_mode ) ) {
++		if ( getenv("DEBUG") != NULL )
++	    	i_info( "sieve-storage: nothing to rescue %s.", storage->active_path);
++    	return TRUE; /* Nothing to rescue */
++  	}
++
++	/* Only regular files can be rescued */
++	if ( S_ISREG( st.st_mode ) ) {
++		const char *dstpath;
++
++ 		t_push();
++
++		dstpath = t_strconcat
++			( storage->dir, "/dovecot.orig.sieve", NULL );
++		if ( file_copy(storage->active_path, dstpath, 1) < 1 ) {
++			sieve_storage_set_critical(storage, 
++				"Active sieve script file '%s' is a regular file and copying it to the "
++				"script storage as '%s' failed. This needs to be fixed manually.",
++				storage->active_path, dstpath);
++			t_pop();
++			return FALSE;	
++		} else {
++			i_info("Moved active sieve script file '%s' to script storage as '%s'.",
++				storage->active_path, dstpath); 
++			t_pop();
++			return TRUE;
++    	}
++		t_pop();
++  	}
++
++	sieve_storage_set_critical( storage,
++		"Active sieve script file '%s' is no symlink nor a regular file. "
++		"This needs to be fixed manually.", storage->active_path );
++	return FALSE;	
++}
++
++int sieve_storage_deactivate(struct sieve_storage *storage)
++{
++	int ret;
++
++	if ( !sieve_storage_rescue_regular_file(storage) ) 
++		return -1;
++
++	/* Delete the symlink, so no script is active */
++	ret = unlink(storage->active_path);
++
++	if ( ret < 0 ) {
++		if ( errno != ENOENT ) {
++			sieve_storage_set_error(storage, "sieve_storage_deactivate(): "
++				"error on unlink(%s): %m", storage->active_path);
++			return -1;
++		} else 
++		  return 0;
++	} 
++
++	return 1;
++}
++
++int
++sieve_script_activate(struct sieve_script *script)
++{
++	struct stat st;
++	const char *active_path_new, *script_path;
++	struct timeval *tv, tv_now;
++	const char *aname;
++	int activated = 0;
++	int ret;
++
++	t_push();	
++
++	/* Find out whether there is an active script, but recreate
++	 * the symlink either way. This way, any possible error in the symlink
++	 * resolves automatically. This step is only necessary to provide a
++	 * proper return value indicating whether the script was already active.
++	 */
++	aname = sieve_storage_get_active_scriptname(script->storage);
++
++	/* Is the requested script already active? */
++	if ( aname == NULL || strcmp(script->name, aname) != 0 ) 
++		activated = 1; 
++
++	/* Check the scriptfile we are trying to activate */
++	if ( lstat(script->filename, &st) != 0 ) {
++		sieve_storage_set_critical(script->storage, 
++		  "Stat on sieve script %s failed, but it is to be activated: %m.", script->name);
++		t_pop();
++		return -1;
++	}
++
++	/* Rescue a possible .dovecot.sieve regular file remaining from old 
++	 * installations.
++	 */
++	if ( !sieve_storage_rescue_regular_file(script->storage) ) {
++		/* Rescue failed, manual intervention is necessary */
++		t_pop();
++		return -1;
++	}
++
++	/* Just try to create the symlink first */
++	script_path = t_strconcat
++	  ( script->storage->link_path, script->name, ".sieve", NULL );
++		
++ 	ret = symlink(script_path, script->storage->active_path);
++
++	if ( ret < 0 ) {
++		if ( errno == EEXIST ) {
++			/* The symlink already exists, try to replace it */
++			tv = &ioloop_timeval;
++
++			for (;;) {	
++				/* First the new symlink is created with a different filename */
++				active_path_new = t_strdup_printf
++					("%s-new.%s.P%sM%s.%s.sieve",
++						script->storage->active_path,
++						dec2str(tv->tv_sec), my_pid,
++						dec2str(tv->tv_usec), my_hostname);
++
++				ret = symlink(script_path, active_path_new);
++		
++				if ( ret < 0 ) {
++					/* If link exists we try again later */
++					if ( errno == EEXIST ) {
++						/* Wait and try again - very unlikely */
++						sleep(2);
++						tv = &tv_now;
++						if (gettimeofday(&tv_now, NULL) < 0)
++							i_fatal("gettimeofday(): %m");
++						continue;
++					}
++
++					/* Other error, critical */
++					sieve_storage_set_critical
++					  (script->storage, 
++					   "Creating symlink() %s to %s failed: %m", 
++					   active_path_new, script_path);
++					t_pop();
++					return -1;
++				}
++	
++				/* Link created */
++				break;
++			}
++
++			/* Replace the existing link and thus activating the new script */
++			ret = rename(active_path_new, script->storage->active_path);
++
++			if ( ret < 0 ) {
++				/* Failed; created symlink must be deleted */
++				(void)unlink(active_path_new);
++				sieve_storage_set_critical
++				  (script->storage,
++				   "Performing rename() %s to %s failed: %m", 
++				   active_path_new, script->storage->active_path);
++				t_pop();
++				return -1;
++			}	
++		} else {
++			/* Other error, critical */
++			sieve_storage_set_critical
++				(script->storage,
++					"Creating symlink() %s to %s failed: %m",
++					script->storage->active_path, script_path);
++			t_pop();
++			return -1;
++		}
++	}
++
++	t_pop();
++	return activated;
++}
++
++
++
+diff -r 894f003d9f5f src/lib-sievestorage/sieve-script.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sievestorage/sieve-script.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,49 @@
++#ifndef __SIEVE_FILE_H
++#define __SIEVE_FILE_H
++
++#include "sieve-storage.h"
++
++struct sieve_script;
++
++struct sieve_script *sieve_script_init
++  (struct sieve_storage *storage, const char *scriptname, bool *exists);
++
++void sieve_script_ref(struct sieve_script *script);
++
++void sieve_script_unref(struct sieve_script **script);
++
++const char *sieve_script_name(struct sieve_script *script);
++
++/* sieve_script_filename():
++ * Returns the filename of the script. If the implemented storage
++ * does not actually store scripts as a file it must temporarily be saved
++ * somewhere in /tmp or so. Don't use this function if you can
++ * also work with a stream. (currently not an issue)
++ */
++const char *sieve_script_filename(struct sieve_script *script);
++
++int sieve_script_get_size(struct sieve_script *script, uoff_t *size);
++
++struct istream *
++sieve_script_open(struct sieve_script *script, bool *deleted_r);
++
++const char *sieve_storage_file_get_scriptname
++  (const struct sieve_storage *storage, const char *filename);
++
++const char *
++  sieve_storage_get_active_scriptname(struct sieve_storage *storage);
++
++struct sieve_script *
++  sieve_storage_get_active_script(struct sieve_storage *storage, bool *no_active);
++
++int sieve_script_is_active(struct sieve_script *script);
++
++int sieve_script_delete(struct sieve_script **script);
++
++int sieve_storage_deactivate(struct sieve_storage *storage);
++
++int
++sieve_script_activate(struct sieve_script *script);
++
++#endif
++
+diff -r 894f003d9f5f src/lib-sievestorage/sieve-storage-private.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sievestorage/sieve-storage-private.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,47 @@
++#ifndef __SIEVE_STORAGE_PRIVATE_H
++#define __SIEVE_STORAGE_PRIVATE_H
++
++#include "sieve-storage.h"
++
++enum sieve_storage_flags {
++	/* Print debugging information while initializing the storage */
++	SIEVE_STORAGE_FLAG_DEBUG     = 0x01,
++	/* Use CRLF linefeeds when saving mails. */
++	SIEVE_STORAGE_FLAG_SAVE_CRLF   = 0x02,
++};
++
++#define SIEVE_READ_BLOCK_SIZE (1024*8)
++
++/* All methods returning int return either TRUE or FALSE. */
++struct sieve_storage {
++	pool_t pool;
++	char *name;
++	char *dir;
++
++	/* Private */	
++	char *active_path;
++	char *active_fname;
++	char *link_path;
++	char *error;
++	char *user; /* name of user accessing the storage */
++
++	enum sieve_storage_flags flags;
++};
++
++struct sieve_script {
++	pool_t pool;
++	int refcount;
++
++	struct sieve_storage *storage;
++	const char *name;
++	struct istream *istream;
++	const char *filename;
++	uoff_t size;
++};
++
++struct sieve_script *sieve_script_init_from_file
++	(struct sieve_storage *storage, const char *scriptname,
++	const char *filename, bool *exists);
++
++#endif
++
+diff -r 894f003d9f5f src/lib-sievestorage/sieve-storage.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sievestorage/sieve-storage.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,402 @@
++#include "lib.h"
++#include "home-expand.h"
++#include "ioloop.h"
++#include "mkdir-parents.h"
++#include "sieve-storage-private.h"
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <sys/stat.h>
++#include <ctype.h>
++#include <time.h>
++
++#define SIEVE_SCRIPT_PATH "~/.dovecot.sieve"
++
++#define CREATE_MODE 0770 /* umask() should limit it more */
++
++#define CRITICAL_MSG \
++  "Internal error occured. Refer to server log for more information."
++#define CRITICAL_MSG_STAMP CRITICAL_MSG " [%Y-%m-%d %H:%M:%S]"
++
++static const char *sieve_get_active_script_path(void)
++{
++  const char *script_path, *home;
++
++  home = getenv("HOME");
++
++  /* userdb may specify Sieve path */
++  script_path = getenv("SIEVE");
++  if (script_path != NULL) {
++	if (*script_path == '\0') {
++		/* disabled */
++		return NULL;
++	}
++
++    if ( *script_path != '/' && *script_path != '~') {
++      /* relative path. change to absolute. */
++      script_path = t_strconcat(getenv("HOME"), "/",  
++        script_path, NULL);
++    }
++  } else {
++    if (home == NULL) {
++      /* we must have a home directory */
++      i_error("sieve-storage: userdb(%s) didn't return a home directory or "
++        "sieve script location, can't find it",
++        getenv("USER"));
++      return NULL;
++    }
++
++    script_path = SIEVE_SCRIPT_PATH;
++  }
++
++  /* No need to check for existance here */
++
++  return script_path;
++}
++
++/* Obtain the directory for script storage from the mail location
++ */
++static const char *sieve_storage_get_dir_from_mail(const char *data)
++{
++	bool debug = (getenv("DEBUG") != NULL);
++	struct stat st;
++	size_t len;
++	const char *root_dir, *dir, *p, *d;
++
++	root_dir = dir = d = NULL;
++
++	if (debug)
++		i_info("sieve-storage: using mail-data: %s", data);
++
++	/* check if we're in the form of mailformat:data
++	   (eg. maildir:Maildir) */
++	p = data;
++	while (i_isalnum(*p)) p++;
++	
++	if (*p == ':') {
++		d = p+1;
++	} else {
++		d = data;
++	}
++
++	if (d == NULL || *d == '\0') {
++		/* Ok, this is bad. Check whether we might be chrooted, bail out otherwise */
++		if (access("/sieve", R_OK|W_OK|X_OK) == 0)
++			root_dir = "/";
++		else {
++			i_error("sieve-storage: sieve storage directory not given "
++				"and mail root provides no alternative.");
++            return NULL;
++		}
++	} else {
++		/* <scriptdir> */
++		p = strchr(d, ':');
++		if (p == NULL)
++			/* No additional parameters */
++			root_dir = d;
++		else {
++			dir = t_strdup_until(d, p);
++ 
++			do {
++				p++;
++				/* Use control dir as script dir if specified */
++				if (strncmp(p, "CONTROL=", 8) == 0)
++					root_dir = t_strcut(p+8, ':');
++				p = strchr(p, ':');
++			} while (p != NULL);
++			
++			if ( root_dir == NULL || *root_dir == '\0' )
++				root_dir = dir;
++		}
++	}
++
++	/* Not found */
++	if ( root_dir == NULL || *root_dir == '\0' ) {
++		if (debug)
++            i_info("sieve-storage: couldn't find root dir from mail-data.");
++        return NULL;
++    }
++
++	/* Strip trailing '/' */
++    len = strlen(root_dir);
++    if (root_dir[len-1] == '/')
++        root_dir = t_strndup(root_dir, len-1);
++
++	/* Superior mail directory must exist; it is never auto-created by the 
++	 * sieve-storage.
++ 	 */
++	if (stat(root_dir, &st) < 0 ) {
++		if ( errno != ENOENT ) {
++			i_error("sieve-storage: stat(%s) failed: %m", root_dir);
++			return NULL;
++		} else {
++			i_error("sieve-storage: root directory specified by "
++				"mail data does not exist: %s", root_dir);
++			return NULL;
++		}
++	} 
++
++	/* Never store scripts directly in the root of the mail or mail:CONTROl directory.
++	 */
++	root_dir = t_strconcat( root_dir, "/sieve", NULL );
++
++	return root_dir;
++}
++
++static const char *sieve_storage_get_relative_link_path
++	(const char *active_path, const char *storage_dir) 
++{
++	const char *link_path, *p;
++	size_t pathlen;
++	
++	/* Determine to what extent the sieve storage and active script 
++	 * paths match up. This enables the managed symlink to be short and the 
++	 * sieve storages can be moved around without trouble (if the active 
++	 * script path is common to the script storage).
++	 */		
++	p = strrchr(active_path, '/');
++	if ( p == NULL ) {
++		link_path = storage_dir;
++	} else { 
++		pathlen = p - active_path;
++
++		if ( strncmp( active_path, storage_dir, pathlen ) == 0 &&
++			(storage_dir[pathlen] == '/' || storage_dir[pathlen] == '\0') ) 
++		{
++			if ( storage_dir[pathlen] == '\0' ) 
++				link_path = ""; 
++			else 
++				link_path = storage_dir + pathlen + 1;
++		} else 
++			link_path = storage_dir;
++	}
++
++	/* Add trailing '/' when link path is not empty 
++	 */
++	pathlen = strlen(link_path);
++    if ( pathlen != 0 && link_path[pathlen-1] != '/')
++        return t_strconcat(link_path, "/", NULL);
++
++	return t_strdup(link_path);
++}
++
++struct sieve_storage *sieve_storage_create_from_mail(const char *data, const char *user)
++{
++	struct sieve_storage *storage;
++	const char *storage_dir;
++
++	t_push();
++
++	storage_dir = sieve_storage_get_dir_from_mail(data);
++	if (storage_dir == NULL) {
++		if (getenv("DEBUG") != NULL)
++			i_info("sieve-storage: failed to obtain storage directory from mail-data.");
++		t_pop();
++		return NULL;
++	} 
++
++	storage = sieve_storage_create(storage_dir, user);
++
++	t_pop();
++
++	return storage;
++}
++
++struct sieve_storage *sieve_storage_create(const char *data, const char *user)
++{
++	bool debug = (getenv("DEBUG") != NULL);
++	pool_t pool;
++	struct sieve_storage *storage;
++	const char *home, *tmp_dir, *link_path, *path;
++	const char *active_path, *active_fname, *storage_dir;
++
++	t_push();
++
++	/* Find out where the active script is stored (e.g. ~/.dovecot.sieve) */
++
++	active_path = sieve_get_active_script_path();
++	if (active_path == NULL) {
++		t_pop();
++		return NULL;
++	}
++
++	if (debug)
++		i_info("sieve-storage: using active sieve script path: %s", active_path);
++
++	/* Get the filename for the active script link */
++	active_fname = strrchr(active_path, '/');
++	if ( active_fname == NULL ) 
++		active_fname = active_path;
++	else
++		active_fname++;
++
++	if ( *active_fname == '\0' ) {	
++		/* Link cannot be just a path */
++		i_error("sieve-storage: Path to active symlink must include "
++			"the link's filename. Path is: %s", active_path);
++
++		t_pop();
++		return NULL;
++	}
++
++	if (debug)
++		i_info("sieve-storage: using active sieve script path: %s", active_path);
++
++	/* Find out where to put the script storage */
++
++	storage_dir = NULL;
++
++	if ( data == NULL || *data == '\0' ) {
++		/* We'll need to figure out the storage location ourself.
++		 *
++         * It's $HOME/sieve or /sieve when (presumed to be) chrooted.  
++		 */
++		home = getenv("HOME");
++        if ( home != NULL && *home != '\0' ) {
++			size_t len;
++
++            if (access(home, R_OK|W_OK|X_OK) == 0) {
++                if (debug) {
++                    i_info("sieve-storage: root exists (%s)",
++                           home);
++                }
++
++				/* Check for trailing '/' */
++    			len = strlen(home);
++    			if (home[len-1] == '/')
++            		path = t_strconcat(home, "sieve", NULL);
++				else
++            		path = t_strconcat(home, "/sieve", NULL);
++			
++                storage_dir = path;
++            } else {
++                if (debug) {
++                    i_info("sieve-storage: access(%s, rwx): "
++                           "failed: %m", home);
++                }
++            }
++		} else {
++			if (debug)
++                i_info("maildir: HOME not set");
++        }
++
++		if (access("/sieve", R_OK|W_OK|X_OK) == 0) {
++            storage_dir = "/sieve";
++			if (debug)
++				i_info("sieve-storage: /sieve exists, assuming chroot");
++        }
++	} else {
++		storage_dir = data;
++	}
++
++	if (storage_dir == NULL || *storage_dir == '\0') {
++        if (debug)
++            i_info("sieve-storage: couldn't find storage dir");
++        return NULL;
++    }
++
++	if (debug)
++ 		i_info("sieve-storage: using sieve script storage directory: %s", storage_dir);    
++
++	/* Expand home directoties in path */
++	storage_dir = home_expand(storage_dir);
++	active_path = home_expand(active_path);
++
++	/* Ensure sieve local directory structure exists (full autocreate):
++	 *  This currently currently only consists of a ./tmp direcory
++	 */
++	tmp_dir = t_strconcat( storage_dir, "/tmp", NULL );	
++	if (mkdir_parents(tmp_dir, CREATE_MODE) < 0 && errno != EEXIST) {
++		i_error("sieve-storage: mkdir_parents(%s, CREATE_MODE) failed: %m", tmp_dir);
++		t_pop();
++		return NULL;
++	}
++
++	/* Create storage object */
++	pool = pool_alloconly_create("sieve-storage", 512+256);
++    storage = p_new(pool, struct sieve_storage, 1);	
++	storage->pool = pool;
++	storage->dir = p_strdup(pool, storage_dir);
++	storage->user = p_strdup(pool, user);
++	storage->active_path = p_strdup(pool, active_path);
++	storage->active_fname = p_strdup(pool, active_fname);
++
++	/* Get the path to be prefixed to the script name in the symlink pointing 
++	 * to the active script.
++	 */
++	link_path = sieve_storage_get_relative_link_path
++		(storage->active_path, storage->dir);
++	if (debug)
++		i_info("sieve-storage: relative path to sieve storage in active link: %s", link_path);
++
++	storage->link_path = p_strdup(pool, link_path);
++
++	t_pop();
++	return storage;
++}
++
++void sieve_storage_free(struct sieve_storage *storage)
++{
++	pool_unref(storage->pool);
++}
++
++void sieve_storage_clear_error(struct sieve_storage *storage)
++{
++	i_free(storage->error);
++	storage->error = NULL;
++}
++
++void sieve_storage_set_error(struct sieve_storage *storage, const char *fmt, ...)
++{
++	va_list va;
++
++	sieve_storage_clear_error(storage);
++
++	if (fmt != NULL) {
++		va_start(va, fmt);
++		storage->error = i_strdup_vprintf(fmt, va);
++		va_end(va);
++	}
++}
++
++void sieve_storage_set_internal_error(struct sieve_storage *storage)
++{
++	struct tm *tm;
++	char str[256];
++
++	tm = localtime(&ioloop_time);
++
++	i_free(storage->error);
++	storage->error =
++	  strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ?
++	  i_strdup(str) : i_strdup(CRITICAL_MSG);
++}
++
++void sieve_storage_set_critical(struct sieve_storage *storage,
++             const char *fmt, ...)
++{
++	va_list va;
++	
++	sieve_storage_clear_error(storage);
++	if (fmt != NULL) {
++		va_start(va, fmt);
++		i_error("sieve-storage: %s", t_strdup_vprintf(fmt, va));
++		va_end(va);
++		
++		/* critical errors may contain sensitive data, so let user
++		   see only "Internal error" with a timestamp to make it
++		   easier to look from log files the actual error message. */
++		sieve_storage_set_internal_error(storage);
++	}
++}
++
++const char *sieve_storage_get_last_error(struct sieve_storage *storage)
++{
++  /* We get here only in error situations, so we have to return some
++     error. If storage->error is NULL, it means we forgot to set it at
++     some point.. */
++  return storage->error != NULL ? storage->error : "Unknown error";
++}
++
++
+diff -r 894f003d9f5f src/lib-sievestorage/sieve-storage.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/lib-sievestorage/sieve-storage.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,19 @@
++#ifndef __SIEVE_STORAGE_H
++#define __SIEVE_STORAGE_H
++
++struct sieve_storage *sieve_storage_create_from_mail(const char *data, const char *user);
++struct sieve_storage *sieve_storage_create(const char *data, const char *user);
++void sieve_storage_free(struct sieve_storage *storage);
++
++/* Set error message in storage. Critical errors are logged with i_error(),
++   but user sees only "internal error" message. */
++void sieve_storage_clear_error(struct sieve_storage *storage);
++void sieve_storage_set_error(struct sieve_storage *storage,
++          const char *fmt, ...) __attr_format__(2, 3);
++void sieve_storage_set_critical(struct sieve_storage *storage,
++             const char *fmt, ...) __attr_format__(2, 3);
++void sieve_storage_set_internal_error(struct sieve_storage *storage);
++
++const char *sieve_storage_get_last_error(struct sieve_storage *storage);
++
++#endif
+diff -r 894f003d9f5f src/managesieve-login/Makefile.am
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/managesieve-login/Makefile.am	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,29 @@
++pkglibexecdir = $(libexecdir)/dovecot
++
++pkglibexec_PROGRAMS = managesieve-login
++
++AM_CPPFLAGS = \
++	-I$(top_srcdir)/src/lib \
++	-I$(top_srcdir)/src/lib-auth \
++	-I$(top_srcdir)/src/lib-managesieve \
++	-I$(top_srcdir)/src/login-common \
++	-I$(top_srcdir)/src/lib-sieve 
++
++managesieve_login_LDADD = \
++	../login-common/liblogin-common.a \
++	../lib-managesieve/libmanagesieve.a \
++	../lib-sieve/libsieve.la \
++	../lib-sievestorage/libsievestorage.a \
++	../lib-auth/libauth.a \
++	../lib/liblib.a \
++	$(SSL_LIBS)
++
++managesieve_login_SOURCES = \
++	client.c \
++	client-authenticate.c \
++	managesieve-proxy.c
++
++noinst_HEADERS = \
++	client.h \
++	client-authenticate.h \
++	managesieve-proxy.h
+diff -r 894f003d9f5f src/managesieve-login/client-authenticate.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/managesieve-login/client-authenticate.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,333 @@
++#include "common.h"
++#include "base64.h"
++#include "buffer.h"
++#include "ioloop.h"
++#include "istream.h"
++#include "ostream.h"
++#include "safe-memset.h"
++#include "str.h"
++#include "str-sanitize.h"
++
++#include <unistd.h>
++
++#include "managesieve-parser.h"
++#include "managesieve-quote.h"
++#include "auth-client.h"
++#include "client.h"
++#include "client-authenticate.h"
++#include "managesieve-proxy.h"
++
++#include <stdlib.h>
++
++/* FIXME: The use of the ANONYMOUS mechanism is currently denied 
++ */
++static bool _sasl_mechanism_acceptable
++	(const struct auth_mech_desc *mech, bool secured) {
++
++	/* a) transport is secured
++	   b) auth mechanism isn't plaintext
++       c) we allow insecure authentication
++	 */
++
++	if ((mech->flags & MECH_SEC_PRIVATE) == 0 &&
++		(mech->flags & MECH_SEC_ANONYMOUS) == 0 &&
++ 		(secured || !disable_plaintext_auth ||
++		(mech->flags & MECH_SEC_PLAINTEXT) == 0)) {
++    		return 1;     
++	}  
++
++	return 0;
++}
++
++const char *client_authenticate_get_capabilities(bool secured)
++{
++	const struct auth_mech_desc *mech;
++	unsigned int i, count;
++	string_t *str;
++
++	str = t_str_new(128);
++	mech = auth_client_get_available_mechs(auth_client, &count);
++
++	if ( count > 0 ) {
++		if ( _sasl_mechanism_acceptable(&(mech[0]), secured) ) {
++			str_append(str, mech[0].name);
++		}
++     
++		for (i = 1; i < count; i++) {
++			if ( _sasl_mechanism_acceptable(&(mech[i]), secured) ) {
++				str_append_c(str, ' ');
++				str_append(str, mech[i].name);
++			}
++		}
++	}
++
++	return str_c(str);
++}
++
++static void client_auth_input(void *context)
++{
++	struct managesieve_client *client = context;
++	struct managesieve_arg *args;
++	const char *msg;
++	char *line;
++	bool fatal;
++
++	if (client->destroyed)
++		return;
++
++	if (!client_read(client))
++		return;
++
++	if (client->skip_line) {
++		if (i_stream_next_line(client->input) == NULL)
++			return;
++
++		client->skip_line = FALSE;
++	}
++
++	switch (managesieve_parser_read_args(client->parser, 0, 0, &args)) {
++	case -1:
++		/* error */
++		msg = managesieve_parser_get_error(client->parser, &fatal);
++		if (fatal) {
++			/* FIXME: What to do? */
++		}
++	  
++		sasl_server_auth_client_error(&client->common, msg);
++		return;
++	case -2:
++		/* not enough data */
++		return;
++	}
++
++	if (args[0].type != MANAGESIEVE_ARG_STRING ||
++		args[1].type != MANAGESIEVE_ARG_EOL) {
++		sasl_server_auth_client_error(&client->common, "Invalid AUTHENTICATE client response.");
++		return;
++	}
++
++	line = MANAGESIEVE_ARG_STR(&args[0]);
++
++	/* Disable input for now */
++	if (client->io != NULL)
++		io_remove(&client->io);
++
++    auth_client_request_continue(client->common.auth_request, line);
++
++	/* clear sensitive data */
++	safe_memset(line, 0, strlen(line));
++}
++
++static bool client_handle_args(struct managesieve_client *client,
++			       const char *const *args, bool success)
++{
++	const char *reason = NULL, *host = NULL, *destuser = NULL, *pass = NULL;
++	string_t *resp_code;
++	unsigned int port = 2000;
++	bool proxy = FALSE, temp = FALSE, nologin = !success;
++
++	for (; *args != NULL; args++) {
++		if (strcmp(*args, "nologin") == 0)
++			nologin = TRUE;
++		else if (strcmp(*args, "proxy") == 0)
++			proxy = TRUE;
++		else if (strcmp(*args, "temp") == 0)
++			temp = TRUE;
++		else if (strncmp(*args, "reason=", 7) == 0)
++			reason = *args + 7;
++		else if (strncmp(*args, "host=", 5) == 0)
++			host = *args + 5;
++		else if (strncmp(*args, "port=", 5) == 0)
++			port = atoi(*args + 5);
++		else if (strncmp(*args, "destuser=", 9) == 0)
++			destuser = *args + 9;
++		else if (strncmp(*args, "pass=", 5) == 0)
++			pass = *args + 5;
++	}
++
++	if (destuser == NULL)
++		destuser = client->common.virtual_user;
++
++  	if (proxy) {
++		/* we want to proxy the connection to another server.
++		don't do this unless authentication succeeded. with
++		master user proxying we can get FAIL with proxy still set.
++
++		proxy host=.. [port=..] [destuser=..] pass=.. */
++
++		if (!success)
++			return FALSE;
++		if (managesieve_proxy_new(client, host, port, destuser, pass) < 0)
++			client_destroy_internal_failure(client);
++		return TRUE;
++	}
++
++	if (host != NULL) {
++		/* MANAGESIEVE referral
++
++		   [nologin] referral host=.. [port=..] [destuser=..]
++		   [reason=..]
++
++		   NO (REFERRAL sieve://user;AUTH=mech@host:port/) Can't login.
++		   OK (...) Logged in, but you should use this server instead.
++		   .. [REFERRAL ..] (Reason from auth server)
++		*/
++		resp_code = t_str_new(128);
++		str_printfa(resp_code, "REFERRAL sieve://%s;AUTH=%s@%s",
++			    destuser, client->common.auth_mech_name, host);
++		if (port != 2000)
++			str_printfa(resp_code, ":%u", port);
++
++		if (reason == NULL) {
++			if (nologin)
++				reason = "Try this server instead.";
++			else 
++				reason = "Logged in, but you should use "
++					"this server instead.";
++		}
++
++		if (!nologin) {
++			client_send_okresp(client, str_c(resp_code), reason);
++			client_destroy(client, "Login with referral");
++			return TRUE;
++		}
++		client_send_noresp(client, str_c(resp_code), reason);
++	} else if (nologin) {
++		/* Authentication went ok, but for some reason user isn't
++		   allowed to log in. Shouldn't probably happen. */
++		if (reason != NULL)
++			client_send_no(client, reason);
++		else if (temp)
++			client_send_no(client, AUTH_TEMP_FAILED_MSG);		
++		else
++			client_send_no(client, AUTH_FAILED_MSG);
++	} else {
++		/* normal login/failure */
++		return FALSE;
++	}
++
++	i_assert(nologin);
++
++	managesieve_parser_reset(client->parser);
++
++	if (!client->destroyed) {
++		/* get back to normal client input. */
++		if (client->io != NULL)
++			io_remove(&client->io);
++		client->io = io_add(client->common.fd, IO_READ,
++			client_input, client);
++	}
++
++	return TRUE;
++}
++
++static void sasl_callback(struct client *_client, enum sasl_server_reply reply,
++			  const char *data, const char *const *args)
++{
++	struct managesieve_client *client = (struct managesieve_client *)_client;
++	string_t *str;
++
++	i_assert(!client->destroyed ||
++		reply == SASL_SERVER_REPLY_CLIENT_ERROR ||
++		reply == SASL_SERVER_REPLY_MASTER_FAILED);
++
++	client->skip_line = TRUE;
++
++	switch (reply) {
++	case SASL_SERVER_REPLY_SUCCESS:
++		if (args != NULL) {
++			if (client_handle_args(client, args, TRUE))
++				break;
++		}
++
++		client_destroy(client, "Login");
++		break;
++	case SASL_SERVER_REPLY_AUTH_FAILED:
++	case SASL_SERVER_REPLY_CLIENT_ERROR:
++		if (args != NULL) {
++			if (client_handle_args(client, args, FALSE))
++				break;
++		}
++
++		client_send_no(client, data != NULL ? data : AUTH_FAILED_MSG);
++
++		managesieve_parser_reset(client->parser);
++
++		if (!client->destroyed) {					
++			/* get back to normal client input. */
++			if (client->io != NULL)
++				io_remove(&client->io);
++			client->io = io_add(client->common.fd, IO_READ,
++				    	client_input, client);
++		}
++		break;
++	case SASL_SERVER_REPLY_MASTER_FAILED:
++		client_destroy_internal_failure(client);
++		break;
++	case SASL_SERVER_REPLY_CONTINUE:
++		t_push();
++		str = t_str_new(256);
++		managesieve_quote_append_string(str, data, TRUE);
++		str_append(str, "\r\n");
++				
++		/* don't check return value here. it gets tricky if we try
++		   to call client_destroy() in here. */
++		(void)o_stream_send(client->output, str_c(str), str_len(str));
++		t_pop();
++
++		managesieve_parser_reset(client->parser);
++
++		i_assert(client->io == NULL);
++        client->io = io_add(client->common.fd, IO_READ,
++                    client_auth_input, client);
++        client_auth_input(client);
++		
++		return;
++	}
++
++	client_unref(client);
++}
++
++int cmd_authenticate(struct managesieve_client *client, struct managesieve_arg *args)
++{
++	const char *mech_name, *init_resp = NULL;
++
++	/* one mandatory argument: authentication mechanism name */
++	if (args[0].type != MANAGESIEVE_ARG_STRING)
++		return -1;
++	if (args[1].type != MANAGESIEVE_ARG_EOL) {
++		/* optional SASL initial response */
++		if (args[1].type != MANAGESIEVE_ARG_STRING ||
++		    args[2].type != MANAGESIEVE_ARG_EOL)
++			return -1;
++		init_resp = MANAGESIEVE_ARG_STR(&args[1]);
++	}
++
++	mech_name = MANAGESIEVE_ARG_STR(&args[0]);
++	if (*mech_name == '\0') 
++		return -1;
++
++	/* FIXME: This refuses the ANONYMOUS mechanism. 
++	 *   This can be removed once anonymous login is implemented according to the 
++	 *   draft RFC. - Stephan
++	 */
++	if ( strncasecmp(mech_name, "ANONYMOUS", 9) == 0 ) {
++		client_send_no(client, "ANONYMOUS mechanism is not implemented.");		
++		return 0;
++	}
++
++	client_ref(client);
++	sasl_server_auth_begin(&client->common, "MANAGESIEVE", mech_name,
++			       init_resp, sasl_callback);
++	if (!client->common.authenticating)
++		return 1;
++
++	/* don't handle input until we get the initial auth reply */
++	if (client->io != NULL)
++		io_remove(&client->io);
++
++	managesieve_parser_reset(client->parser);
++
++	return 0;
++}
++
+diff -r 894f003d9f5f src/managesieve-login/client-authenticate.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/managesieve-login/client-authenticate.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,9 @@
++#ifndef __CLIENT_AUTHENTICATE_H
++#define __CLIENT_AUTHENTICATE_H
++
++const char *client_authenticate_get_capabilities(bool secured);
++
++int cmd_login(struct managesieve_client *client, struct managesieve_arg *args);
++int cmd_authenticate(struct managesieve_client *client, struct managesieve_arg *args);
++
++#endif
+diff -r 894f003d9f5f src/managesieve-login/client.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/managesieve-login/client.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,680 @@
++#include "common.h"
++#include "buffer.h"
++#include "hash.h"
++#include "ioloop.h"
++#include "istream.h"
++#include "ostream.h"
++#include "process-title.h"
++#include "safe-memset.h"
++#include "str.h"
++#include "strfuncs.h"
++#include "strescape.h"
++#include "managesieve-parser.h"
++#include "managesieve-quote.h"
++#include "sieve-implementation.h"
++
++#include "client.h"
++#include "client-authenticate.h"
++#include "auth-client.h"
++#include "ssl-proxy.h"
++#include "managesieve-proxy.h"
++
++#include <stdlib.h>
++
++/* max. size of one parameter in line, or max reply length in SASL
++   authentication */
++#define MAX_INBUF_SIZE 4096
++
++/* max. size of output buffer. if it gets full, the client is disconnected.
++   SASL authentication gives the largest output. */
++#define MAX_OUTBUF_SIZE 4096
++
++/* Disconnect client after idling this many seconds */
++#define CLIENT_LOGIN_IDLE_TIMEOUT 60
++
++/* Disconnect client when it sends too many bad commands */
++#define CLIENT_MAX_BAD_COMMANDS 10
++
++/* When max. number of simultaneous connections is reached, few of the
++   oldest connections are disconnected. Since we have to go through the whole
++   client hash, it's faster if we disconnect multiple clients. */
++#define CLIENT_DESTROY_OLDEST_COUNT 16
++
++#if CLIENT_LOGIN_IDLE_TIMEOUT >= AUTH_REQUEST_TIMEOUT
++#  error client idle timeout must be smaller than authentication timeout
++#endif
++
++const char *login_protocol = "MANAGESIEVE";
++const char *capability_string = CAPABILITY_STRING;
++
++const char *managesieve_implementation_string;
++
++static struct hash_table *clients;
++static struct timeout *to_idle;
++
++static void client_set_title(struct managesieve_client *client)
++{
++	const char *addr;
++
++	if (!verbose_proctitle || !process_per_connection)
++		return;
++
++	addr = net_ip2addr(&client->common.ip);
++	if (addr == NULL)
++		addr = "??";
++
++	process_title_set(t_strdup_printf(client->common.tls ?
++					  "[%s TLS]" : "[%s]", addr));
++}
++
++static void client_open_streams(struct managesieve_client *client, int fd)
++{
++	client->input = i_stream_create_file(fd, default_pool,
++					     MAX_INBUF_SIZE, FALSE);
++	client->output = o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE,
++					      FALSE);
++	client->parser = managesieve_parser_create(client->input, client->output,
++					    MAX_MANAGESIEVE_LINE);
++}
++
++/* Skip incoming data until newline is found,
++   returns TRUE if newline was found. */
++bool client_skip_line(struct managesieve_client *client)
++{
++	const unsigned char *data;
++	size_t i, data_size;
++
++	data = i_stream_get_data(client->input, &data_size);
++
++	for (i = 0; i < data_size; i++) {
++		if (data[i] == '\n') {
++			i_stream_skip(client->input, i+1);
++			return TRUE;
++		}
++	}
++
++	return FALSE;
++}
++
++static void client_send_capabilities(struct managesieve_client *client)
++{
++	const char *auths;
++	const char *sievecap, *sieveimpl;
++
++	sievecap = sieve_get_capabilities();
++	if (sievecap == NULL)
++	  	sievecap = "";
++
++	t_push();
++	sievecap = t_strconcat("\"SIEVE\" \"", sievecap, "\"", NULL);
++	sieveimpl = t_strconcat("\"IMPLEMENTATION\" \"",
++    managesieve_implementation_string, "\"", NULL);
++
++	auths = client_authenticate_get_capabilities(client->common.secured);
++
++	/* We assume no MANAGESIEVE-string incompatible values are produced here */
++	client_send_line(client, sieveimpl);
++	client_send_line(client, t_strconcat("\"SASL\" \"", auths, "\"", NULL) );
++	client_send_line(client, sievecap);
++
++	if (ssl_initialized && !client->common.tls)
++		client_send_line(client, "\"STARTTLS\"" );
++
++	t_pop();
++}
++
++static int cmd_capability(struct managesieve_client *client)
++{
++	client_send_capabilities(client);
++	client_send_ok(client, "Capability completed.");
++	return TRUE;
++}
++
++static void client_start_tls(struct managesieve_client *client)
++{
++	int fd_ssl;
++
++    client_ref(client);
++    connection_queue_add(1);
++    if (!client_unref(client) || client->destroyed)
++        return;
++
++	fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip,
++			       &client->common.proxy);
++	if (fd_ssl == -1) {
++		client_send_bye(client, "TLS initialization failed.");
++		client_destroy(client, "TLS initialization failed.");
++		return;
++	}
++
++	client->common.tls = TRUE;
++	client->common.secured = TRUE;
++	client_set_title(client);
++
++	client->common.fd = fd_ssl;
++	i_stream_unref(&client->input);
++	o_stream_unref(&client->output);
++	managesieve_parser_destroy(&client->parser);
++
++	/* CRLF is lost from buffer when streams are reopened. */
++	client->skip_line = FALSE;
++
++	client_open_streams(client, fd_ssl);
++	client->io = io_add(client->common.fd, IO_READ, client_input, client);
++}
++
++static int client_output_starttls(void *context)
++{
++	struct managesieve_client *client = context;
++	int ret;
++
++	if ((ret = o_stream_flush(client->output)) < 0) {
++		client_destroy(client, "Disconnected");
++		return 1;
++	}
++
++	if (ret > 0) {
++		o_stream_set_flush_callback(client->output, NULL, NULL);
++		client_start_tls(client);
++	}
++	return 1;
++}
++
++static int cmd_starttls(struct managesieve_client *client)
++{
++	if (client->common.tls) {
++		client_send_no(client, "TLS is already active.");
++		return 1;
++	}
++
++	if (!ssl_initialized) {
++		client_send_no(client, "TLS support isn't enabled.");
++		return 1;
++	}
++
++	/* remove input handler, SSL proxy gives us a new fd. we also have to
++	   remove it in case we have to wait for buffer to be flushed */
++	if (client->io != NULL)
++		io_remove(&client->io);
++
++	client_send_ok(client, "Begin TLS negotiation now.");
++
++	/* uncork the old fd */
++	o_stream_uncork(client->output);
++
++	if (o_stream_flush(client->output) <= 0) {
++		/* the buffer has to be flushed */
++		o_stream_set_flush_pending(client->output, TRUE);
++		o_stream_set_flush_callback(client->output,
++					    client_output_starttls, client);
++	} else {
++		client_start_tls(client);
++	}
++
++    /* Cork the stream to send the capability data as a single tcp frame
++     *   Some naive clients break if we don't.
++     */
++    o_stream_cork(client->output);
++
++	client_send_capabilities(client);
++	client_send_ok(client, "TLS negotiation successful.");
++
++    o_stream_uncork(client->output);
++
++	return 1;
++}
++
++static int cmd_logout(struct managesieve_client *client)
++{
++	client_send_ok(client, "Logout completed.");
++	client_destroy(client, "Aborted login (logout command)");
++	return 1;
++}
++
++static int client_command_execute(struct managesieve_client *client, const char *cmd,
++				  struct managesieve_arg *args)
++{
++	cmd = t_str_ucase(cmd);
++	if (strcmp(cmd, "AUTHENTICATE") == 0)
++		return cmd_authenticate(client, args);
++	if (strcmp(cmd, "CAPABILITY") == 0)
++		return cmd_capability(client);
++	if (strcmp(cmd, "STARTTLS") == 0)
++		return cmd_starttls(client);
++	if (strcmp(cmd, "LOGOUT") == 0)
++		return cmd_logout(client);
++
++	return -1;
++}
++
++static bool client_handle_input(struct managesieve_client *client)
++{
++	struct managesieve_arg *args;
++	const char *msg;
++	int ret;
++	bool fatal;
++
++	i_assert(!client->common.authenticating);
++
++	if (client->cmd_finished) {
++		/* clear the previous command from memory. don't do this
++		   immediately after handling command since we need the
++		   cmd_tag to stay some time after authentication commands. */
++		client->cmd_name = NULL;
++		managesieve_parser_reset(client->parser);
++
++		/* remove \r\n */
++		if (client->skip_line) {
++			if (!client_skip_line(client))
++				return FALSE;
++			client->skip_line = FALSE;
++		}
++
++		client->cmd_finished = FALSE;
++	}
++
++	if (client->cmd_name == NULL) {
++		client->cmd_name = managesieve_parser_read_word(client->parser);
++		if (client->cmd_name == NULL)
++			return FALSE; /* need more data */
++	}
++
++	switch (managesieve_parser_read_args(client->parser, 0, 0, &args)) {
++	case -1:
++		/* error */
++		msg = managesieve_parser_get_error(client->parser, &fatal);
++		if (fatal) {
++			client_send_bye(client, msg);
++			client_destroy(client, t_strconcat("Disconnected: ",
++							   msg, NULL));
++			return FALSE;
++		}
++
++		client_send_no(client, msg);
++		client->cmd_finished = TRUE;
++		client->skip_line = TRUE;
++		return TRUE;
++	case -2:
++		/* not enough data */
++		return FALSE;
++	}
++	client->skip_line = TRUE;
++
++	ret = client_command_execute(client, client->cmd_name, args);
++
++	client->cmd_finished = TRUE;
++	if (ret < 0) {
++		if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
++			client_send_bye(client,	
++				"Too many invalid MANAGESIEVE commands.");
++			client_destroy(client, "Disconnected: "
++				"Too many invalid commands.");
++			return FALSE;
++		}  
++		client_send_no(client,
++			"Error in MANAGESIEVE command received by server.");
++	}
++
++	return ret != 0;
++}
++
++bool client_read(struct managesieve_client *client)
++{
++	switch (i_stream_read(client->input)) {
++	case -2:
++		/* buffer full */
++		client_send_bye(client, "Input buffer full, aborting");
++		client_destroy(client, "Disconnected: Input buffer full");
++		return FALSE;
++	case -1:
++		/* disconnected */
++		client_destroy(client, "Disconnected");
++		return FALSE;
++	default:
++		/* something was read */
++		return TRUE;
++	}
++}
++
++void client_input(void *context)
++{
++	struct managesieve_client *client = context;
++
++	client->last_input = ioloop_time;
++
++	if (!client_read(client))
++		return;
++
++	client_ref(client);
++
++	if (!auth_client_is_connected(auth_client)) {
++		/* we're not yet connected to auth process -
++		   don't allow any commands */
++		/* FIXME: Can't do this with managesieve. Any other ways?
++		client_send_ok(client,
++		"Waiting for authentication process to respond.");
++		*/
++		client->input_blocked = TRUE;
++	} else {
++		o_stream_cork(client->output);
++		while (client_handle_input(client)) ;
++		o_stream_uncork(client->output);
++	}
++
++	client_unref(client);
++}
++
++void client_destroy_oldest(void)
++{
++	struct hash_iterate_context *iter;
++	void *key, *value;
++	struct managesieve_client *destroy_buf[CLIENT_DESTROY_OLDEST_COUNT];
++	int i, destroy_count;
++
++	/* find the oldest clients and put them to destroy-buffer */
++	memset(destroy_buf, 0, sizeof(destroy_buf));
++
++ 	destroy_count = max_connections > CLIENT_DESTROY_OLDEST_COUNT*2 ?
++        CLIENT_DESTROY_OLDEST_COUNT : I_MIN(max_connections/2, 1);
++    iter = hash_iterate_init(clients);
++    while (hash_iterate(iter, &key, &value)) {
++        struct managesieve_client *client = key;
++
++        for (i = 0; i < destroy_count; i++) {
++            if (destroy_buf[i] == NULL ||
++                destroy_buf[i]->created > client->created) {
++                /* @UNSAFE */
++                memmove(destroy_buf+i+1, destroy_buf+i,
++                    sizeof(destroy_buf) -
++                    (i+1) * sizeof(struct managesieve_client *));
++                destroy_buf[i] = client;
++                break;
++            }
++        }
++    }
++    hash_iterate_deinit(iter);
++
++    /* then kill them */
++    for (i = 0; i < destroy_count; i++) {
++        if (destroy_buf[i] == NULL)
++            break;
++
++        client_destroy(destroy_buf[i],
++                   "Disconnected: Connection queue full");
++    }
++}
++
++static void client_send_greeting(struct managesieve_client *client)
++{
++	/* Cork the stream to send the capability data as a single tcp frame
++     *   Some naive clients break if we don't.
++     */
++    o_stream_cork(client->output);
++
++  	/* Send initial capabilities */   
++  	client_send_capabilities(client);
++	client_send_ok(client, greeting);
++	client->greeting_sent = TRUE;
++
++    o_stream_uncork(client->output);
++}
++
++struct client *client_create(int fd, bool ssl, const struct ip_addr *local_ip,
++			     const struct ip_addr *ip)
++{
++  struct managesieve_client *client;
++
++  i_assert(fd != -1);
++
++  connection_queue_add(1);
++
++  /* always use nonblocking I/O */
++  net_set_nonblock(fd, TRUE);
++
++  client = i_new(struct managesieve_client, 1);
++  client->created = ioloop_time;
++  client->refcount = 1;
++  client->common.tls = ssl;
++  client->common.secured = ssl || net_ip_compare(ip, local_ip);
++
++  client->common.local_ip = *local_ip;
++  client->common.ip = *ip;
++  client->common.fd = fd;
++
++  client_open_streams(client, fd);
++  client->io = io_add(fd, IO_READ, client_input, client);
++
++  client->last_input = ioloop_time;
++  hash_insert(clients, client, client);
++
++  main_ref();
++
++  if (!greeting_capability || auth_client_is_connected(auth_client))
++                client_send_greeting(client);
++  client_set_title(client);
++
++  return &client->common;
++}
++
++void client_destroy(struct managesieve_client *client, const char *reason)
++{
++	if (client->destroyed)
++		return;
++	client->destroyed = TRUE;
++
++	if (reason != NULL)
++		client_syslog(&client->common, reason);
++
++	hash_remove(clients, client);
++
++	if (client->input != NULL)
++		i_stream_close(client->input);
++	if (client->output != NULL)
++		o_stream_close(client->output);
++
++	if (client->common.master_tag != 0)
++        master_request_abort(&client->common);
++
++    if (client->common.auth_request != NULL) {
++        i_assert(client->common.authenticating);
++        sasl_server_auth_client_error(&client->common, NULL);
++    } else {
++        i_assert(!client->common.authenticating);
++    }
++
++	if (client->io != NULL)
++		io_remove(&client->io);
++   
++	if (client->common.fd != -1) {
++		net_disconnect(client->common.fd);
++		client->common.fd = -1;
++	}
++
++	if (client->proxy_password != NULL) {
++        safe_memset(client->proxy_password, 0,
++                strlen(client->proxy_password));
++        i_free(client->proxy_password);
++        client->proxy_password = NULL;
++    }
++
++    i_free(client->proxy_user);
++    client->proxy_user = NULL;
++
++    if (client->proxy != NULL) {
++        login_proxy_free(client->proxy);
++        client->proxy = NULL;
++    }
++
++    if (client->common.proxy != NULL) {
++        ssl_proxy_free(client->common.proxy);
++        client->common.proxy = NULL;
++    }
++
++    client_unref(client);
++
++    main_listen_start();
++    main_unref();
++}
++
++void client_destroy_internal_failure(struct managesieve_client *client)
++{
++	client_send_byeresp(client, "TRYLATER", "Internal login failure. "
++		"Refer to server log for more information.");
++	client_destroy(client, "Internal login failure");
++}
++
++void client_ref(struct managesieve_client *client)
++{
++	client->refcount++;
++}
++
++bool client_unref(struct managesieve_client *client)
++{
++	i_assert(client->refcount > 0);
++	if (--client->refcount > 0)
++		return TRUE;
++
++	i_assert(client->destroyed);
++
++	managesieve_parser_destroy(&client->parser);
++
++	if (client->input != NULL)
++		i_stream_unref(&client->input);
++	if (client->output != NULL)
++		o_stream_unref(&client->output);
++
++	i_free(client->common.virtual_user);
++	i_free(client->common.auth_mech_name);
++	i_free(client);
++
++	return FALSE;
++}
++
++void client_send_line(struct managesieve_client *client, const char *line)
++{
++	struct const_iovec iov[2];
++	ssize_t ret;
++
++	iov[0].iov_base = line;
++	iov[0].iov_len = strlen(line);
++	iov[1].iov_base = "\r\n";
++	iov[1].iov_len = 2;
++
++	ret = o_stream_sendv(client->output, iov, 2);
++	if (ret < 0 || (size_t)ret != iov[0].iov_len + iov[1].iov_len) {
++		/* either disconnection or buffer full. in either case we
++		   want this connection destroyed. however destroying it here
++		   might break things if client is still tried to be accessed
++		   without being referenced.. */
++		i_stream_close(client->input);
++	}
++}
++
++void _client_send_response(struct managesieve_client *client, 
++	const char *oknobye, const char *resp_code, const char *msg)
++{
++	string_t *str;
++
++	str = t_str_new(128);
++	str_append(str, oknobye);
++
++	if ( resp_code != NULL )
++	{
++		str_append(str, " (");
++		str_append(str, resp_code);
++		str_append_c(str, ')');
++	}
++
++	if ( msg != NULL )	
++	{
++		str_append_c(str, ' ');
++		managesieve_quote_append_string(str, msg, TRUE);
++	}
++
++	client_send_line(client, str_c(str));
++}
++
++static void client_check_idle(struct managesieve_client *client)
++{
++	if (ioloop_time - client->last_input >= CLIENT_LOGIN_IDLE_TIMEOUT) {
++		client_send_bye(client, "Disconnected for inactivity.");
++		client_destroy(client, "Disconnected: Inactivity");
++	}
++}
++
++static void idle_timeout(void *context __attr_unused__)
++{
++	struct hash_iterate_context *iter;
++	void *key, *value;
++
++	iter = hash_iterate_init(clients);
++	while (hash_iterate(iter, &key, &value)) {
++		struct managesieve_client *client = key;
++
++		client_check_idle(client);
++	}
++	hash_iterate_deinit(iter);
++}
++
++unsigned int clients_get_count(void)
++{
++	return hash_size(clients);
++}
++
++void clients_notify_auth_connected(void)
++{
++	struct hash_iterate_context *iter;
++	void *key, *value;
++
++	iter = hash_iterate_init(clients);
++	while (hash_iterate(iter, &key, &value)) {
++		struct managesieve_client *client = key;
++
++		if (!client->greeting_sent)
++			client_send_greeting(client);
++		if (client->input_blocked) {
++			client->input_blocked = FALSE;
++			client_input(client);
++		}
++	}
++	hash_iterate_deinit(iter);
++}
++
++void clients_destroy_all(void)
++{
++	struct hash_iterate_context *iter;
++	void *key, *value;
++
++	iter = hash_iterate_init(clients);
++	while (hash_iterate(iter, &key, &value)) {
++		struct managesieve_client *client = key;
++
++		client_destroy(client,  "Disconnected: Shutting down");
++	}
++	hash_iterate_deinit(iter);
++}
++
++void clients_init(void)
++{
++	const char *str;
++
++	clients = hash_create(default_pool, default_pool, 128, NULL, NULL);
++	to_idle = timeout_add(1000, idle_timeout, NULL);
++
++	/* Specific MANAGESIEVE settings */
++	str = getenv("MANAGESIEVE_IMPLEMENTATION_STRING");
++	managesieve_implementation_string = str != NULL ?
++    	str : DEFAULT_MANAGESIEVE_IMPLEMENTATION_STRING;
++
++	sieve_init();
++	sieve_set_implementation("cmu");
++}
++
++void clients_deinit(void)
++{
++	clients_destroy_all();
++	hash_destroy(clients);
++
++	timeout_remove(&to_idle);
++	sieve_deinit();
++}
+diff -r 894f003d9f5f src/managesieve-login/client.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/managesieve-login/client.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,72 @@
++#ifndef __CLIENT_H
++#define __CLIENT_H
++
++#include "network.h"
++#include "master.h"
++#include "client-common.h"
++
++/* FIXME: Duplicate, also defined in src/managesieve */
++#define DEFAULT_MANAGESIEVE_IMPLEMENTATION_STRING PACKAGE
++
++/* maximum length for MANAGESIEVE command line. */
++#define MAX_MANAGESIEVE_LINE 8192
++
++struct managesieve_client {
++	struct client common;
++
++	time_t created;
++	int refcount;
++
++	struct io *io;
++	struct istream *input;
++	struct ostream *output;
++	struct managesieve_parser *parser;
++
++	struct login_proxy *proxy;
++	char *proxy_user, *proxy_password;
++
++	time_t last_input;
++	unsigned int bad_counter;
++
++	const char *cmd_name;
++
++	unsigned int cmd_finished:1;
++	unsigned int skip_line:1;
++	unsigned int input_blocked:1;
++	unsigned int destroyed:1;
++	unsigned int greeting_sent:1;
++
++	/* Maybe these should be combined in some enum state variable */
++	unsigned int proxy_greeting_recvd:1;  
++ 	unsigned int proxy_login_sent:1;
++};
++
++void client_destroy(struct managesieve_client *client, const char *reason);
++void client_destroy_internal_failure(struct managesieve_client *client);
++
++void client_send_line(struct managesieve_client *client, const char *line);
++
++bool client_read(struct managesieve_client *client);
++bool client_skip_line(struct managesieve_client *client);
++void client_input(void *context);
++
++void client_ref(struct managesieve_client *client);
++bool client_unref(struct managesieve_client *client);
++
++void _client_send_response(struct managesieve_client *client,
++  const char *oknobye, const char *resp_code, const char *msg);
++
++#define client_send_ok(client, msg) \
++	_client_send_response(client, "OK", NULL, msg)
++#define client_send_no(client, msg) \
++  _client_send_response(client, "NO", NULL, msg)
++#define client_send_bye(client, msg) \
++  _client_send_response(client, "BYE", NULL, msg)
++
++#define client_send_okresp(client, resp_code, msg) \
++  _client_send_response(client, "OK", resp_code, msg)
++#define client_send_noresp(client, resp_code, msg) \
++  _client_send_response(client, "NO", resp_code, msg)
++#define client_send_byeresp(client, resp_code, msg) \
++  _client_send_response(client, "BYE", resp_code, msg)
++#endif
+diff -r 894f003d9f5f src/managesieve-login/managesieve-proxy.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/managesieve-login/managesieve-proxy.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,299 @@
++/* Copyright (C) 2004 Timo Sirainen */
++
++#include <string.h>
++#include "common.h"
++#include "ioloop.h"
++#include "istream.h"
++#include "ostream.h"
++#include "str.h"
++#include "safe-memset.h"
++#include "buffer.h"
++#include "base64.h"
++#include "client.h"
++#include "managesieve-quote.h"
++#include "managesieve-proxy.h"
++#include "managesieve-parser.h"
++
++static int proxy_input_line(struct managesieve_client *client,
++			    struct ostream *output, const char *line)
++{
++	string_t *str;
++	const char *msg;
++
++	i_assert(!client->destroyed);
++
++	if (!client->proxy_login_sent) {
++		string_t *plain_login, *base64;
++		struct istream *input;
++		struct managesieve_parser *parser;
++ 		struct managesieve_arg *args;
++		int ret;
++		bool fatal = FALSE, greeting_recvd = FALSE;
++
++		/* Server will send greeting which is actually a capability 
++		 * response. Output from a faulty server should not be accepted,
++		 * so the response is parsed and verified.
++		 */
++
++		/* Build an input stream for the managesieve parser 
++		 *  FIXME: It would be nice if the line-wise parsing could be
++		 *    substituded by something similar to the command line interpreter.
++		 *    However, the current login_proxy structure does not make streams
++		 *    known until inside proxi_input handler.
++		 */
++		line = t_strconcat(line, "\r\n", NULL);
++		input = i_stream_create_from_data(pool_datastack_create(), line,
++			strlen(line));
++		parser = managesieve_parser_create(input, NULL, MAX_MANAGESIEVE_LINE);
++		managesieve_parser_reset(parser);
++
++	    /* Parse input 
++		 *  FIXME: Theoretically the OK response could include a 
++		 *   response code which could be rejected by the parser. 
++		 */ 
++		(void)i_stream_read(input);
++		ret = managesieve_parser_read_args(parser, 2, 0, &args);
++		
++		if ( ret >= 1 ) {
++			if ( args[0].type == MANAGESIEVE_ARG_ATOM &&
++        		strncasecmp(MANAGESIEVE_ARG_STR(&(args[0])), "OK", 2) == 0 ) {
++
++				/* Received OK response; greeting is finished */
++				greeting_recvd = TRUE;
++
++      		} else if ( args[0].type == MANAGESIEVE_ARG_STRING ) {
++        		if ( strncasecmp(MANAGESIEVE_ARG_STR(&(args[0])), "SASL", 4) == 0 ) {
++					/* Check whether the server supports the SASL mechanism 
++		    		 * we are going to use (currently only PLAIN supported). 
++					 */
++					if ( ret == 2 && args[1].type == MANAGESIEVE_ARG_STRING ) {
++						char *p = MANAGESIEVE_ARG_STR(&(args[1]));
++						int mech_found = FALSE;
++								
++						while ( p != NULL ) {
++							if ( strncasecmp(p, "PLAIN", 5) == 0 ) {
++								mech_found = TRUE;
++								break;
++              				}
++
++							p = strchr(p, ' ');
++							if ( p != NULL ) p++;
++						}	 
++
++						if ( !mech_found ) {
++							i_error("managesieve-proxy(%s): "
++			          			"Server does not support required PLAIN SASL mechanism.",
++							client->common.virtual_user);
++
++							fatal = TRUE;
++						} 	
++					}
++				} 	
++			} else {
++				/* Do not accept faulty server */
++        		i_error("managesieve-proxy(%s): "
++          			"Remote returned with invalid capability/greeting line: %s",
++          			client->common.virtual_user, line);
++
++				fatal = TRUE;
++			}
++
++    	} else if ( ret == -2 ) {
++			/* Parser needs more data (not possible on mem stream) */
++			i_unreached();
++
++    	} else if ( ret < 0 ) {
++			const char *error_str = managesieve_parser_get_error(parser, &fatal);
++			error_str = (error_str != NULL ? error_str : "unknown (bug)" );
++	
++			/* Do not accept faulty server */
++			i_error("managesieve-proxy(%s): "
++			"Protocol parse error(%d) in capability/greeting line: %s (line='%s')",
++			client->common.virtual_user, ret, error_str, line);
++	
++			fatal = TRUE;
++		}
++
++		/* Cleanup parser */
++    	managesieve_parser_destroy(&parser);
++	    i_stream_destroy(&input);
++
++		/* Time to exit if greeting was not accepted */
++		if ( fatal ) {			
++			client_destroy_internal_failure(client);
++	
++			return -1;
++		}
++
++		/* Wait until greeting is received completely */
++		if ( !greeting_recvd ) return 0;
++
++		/* Send AUTHENTICATE "PLAIN" command 
++    	 *  FIXME: Currently there seems to be no SASL client implementation,
++		 *    so only implement the trivial PLAIN method 
++		 *    - Stephan
++	     */
++		t_push();
++	
++		/*   Base64-encode the credentials 
++		 * 	   [authorization ID \0 authentication ID \0 pass]
++	     */
++		plain_login = buffer_create_dynamic(pool_datastack_create(), 64);
++		buffer_append_c(plain_login, '\0');
++		buffer_append(plain_login, client->proxy_user, strlen(client->proxy_user));
++	  	buffer_append_c(plain_login, '\0');
++		buffer_append(plain_login, client->proxy_password, strlen(client->proxy_password));
++
++		base64 = buffer_create_dynamic(pool_datastack_create(),
++		MAX_BASE64_ENCODED_SIZE(plain_login->used));
++		base64_encode(plain_login->data, plain_login->used, base64);
++
++		/*   Send command */
++		str = t_str_new(128);
++		str_append(str, "AUTHENTICATE \"PLAIN\" ");
++		managesieve_quote_append_string(str, str_c(base64),  FALSE);
++		str_append(str, "\r\n");
++		(void)o_stream_send(output, str_data(str), str_len(str));
++		
++		/*   Cleanup */
++		t_pop();
++
++		/* Cleanup sensitive data */
++		safe_memset(client->proxy_password, 0,
++			   strlen(client->proxy_password));
++		i_free(client->proxy_password);
++		client->proxy_password = NULL;
++		client->proxy_login_sent = TRUE;
++
++		return 0;
++
++	} else { 
++		if (strncasecmp(line, "OK ", 3) == 0) {
++			/* Login successful. Send this line to client. */
++			o_stream_cork(client->output);
++			(void)o_stream_send_str(client->output, line);
++			(void)o_stream_send(client->output, "\r\n", 2);
++			o_stream_uncork(client->output);
++
++			msg = t_strdup_printf("managesieve-proxy(%s): started proxying to %s:%u",
++				      client->common.virtual_user,
++				      login_proxy_get_host(client->proxy),
++				      login_proxy_get_port(client->proxy));
++
++			(void)client_skip_line(client);
++			login_proxy_detach(client->proxy, client->input,
++				   client->output);
++
++			client->proxy = NULL;
++			client->input = NULL;
++			client->output = NULL;
++			client->common.fd = -1;
++			client_destroy(client, msg);
++
++		} else {
++			/* Login failed. Send our own failure reply so client can't
++		  	 * figure out if user exists or not just by looking at the
++			 * reply string.
++			 */
++			client_send_no(client, AUTH_FAILED_MSG);
++
++			/* allow client input again */
++			i_assert(client->io == NULL);
++			client->io = io_add(client->common.fd, IO_READ,
++				    client_input, client);
++
++			login_proxy_free(client->proxy);
++			client->proxy = NULL;
++
++			i_free(client->proxy_user);
++			client->proxy_user = NULL;
++		}
++
++		return -1;
++	}
++
++	i_unreached();
++	return -1;
++}
++
++static void proxy_input(struct istream *input, struct ostream *output,
++			void *context)
++{
++	struct managesieve_client *client = context;
++	const char *line;
++
++	if (input == NULL) {
++		if (client->io != NULL) {
++			/* remote authentication failed, we're just
++			   freeing the proxy */
++			return;
++		}
++
++		if (client->destroyed) {
++			/* we came here from client_destroy() */
++			return;
++		}
++
++		/* failed for some reason, probably server disconnected */
++		client_send_byeresp(client, "TRYLATER", "Temporary login failure.");
++		client_destroy(client, NULL);
++		return;
++	}
++
++	i_assert(!client->destroyed);
++
++	switch (i_stream_read(input)) {
++	case -2:
++		/* buffer full */
++		i_error("managesieve-proxy(%s): Remote input buffer full",
++			client->common.virtual_user);
++		client_destroy_internal_failure(client);
++		return;
++	case -1:
++		/* disconnected */
++		client_destroy(client, "Proxy: Remote disconnected");
++		return;
++	}
++
++	while ((line = i_stream_next_line(input)) != NULL) {
++		if (proxy_input_line(client, output, line) < 0)
++			break;
++	}
++}
++
++int managesieve_proxy_new(struct managesieve_client *client, const char *host,
++		   unsigned int port, const char *user, const char *password)
++{
++	i_assert(user != NULL);
++	i_assert(!client->destroyed);
++
++	if (password == NULL) {
++		i_error("proxy(%s): password not given",
++			client->common.virtual_user);
++		return -1;
++	}
++
++	i_assert(client->refcount > 1);
++	connection_queue_add(1);
++
++	if (client->destroyed) {
++		/* connection_queue_add() decided that we were the oldest
++		   connection and killed us. */
++		return -1;
++	}
++
++	client->proxy = login_proxy_new(&client->common, host, port,
++					proxy_input, client);
++	if (client->proxy == NULL)
++		return -1;
++
++	client->proxy_login_sent = FALSE;
++	client->proxy_user = i_strdup(user);
++	client->proxy_password = i_strdup(password);
++
++	/* disable input until authentication is finished */
++	if (client->io != NULL)
++		io_remove(&client->io);
++
++	return 0;
++}
+diff -r 894f003d9f5f src/managesieve-login/managesieve-proxy.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/managesieve-login/managesieve-proxy.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,9 @@
++#ifndef __MANAGESIEVE_PROXY_H
++#define __MANAGESIEVE_PROXY_H
++
++#include "login-proxy.h"
++
++int managesieve_proxy_new(struct managesieve_client *client, const char *host,
++		   unsigned int port, const char *user, const char *password);
++
++#endif
+diff -r 894f003d9f5f src/managesieve/Makefile.am
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/managesieve/Makefile.am	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,56 @@
++pkglibexecdir = $(libexecdir)/dovecot
++
++pkglibexec_PROGRAMS = managesieve
++
++AM_CPPFLAGS = \
++	-I$(top_srcdir)/src/lib \
++	-I$(top_srcdir)/src/lib-dict \
++	-I$(top_srcdir)/src/lib-managesieve \
++	-DMODULEDIR=\""$(moduledir)"\" \
++	-I$(top_srcdir)/src/lib-sievestorage \
++	-I$(top_srcdir)/src/lib-sieve
++
++managesieve_LDFLAGS = -export-dynamic
++
++# get some functions included which only plugins use. liblib should probably
++# be a shared library so this wouldn't be needed..
++unused_objects = \
++	../lib/mountpoint.o 
++
++libs = \
++	../lib-managesieve/libmanagesieve.a \
++  ../lib-sievestorage/libsievestorage.a \
++	../lib-sieve/libsieve.la \
++	../lib-dict/libdict.a \
++	../lib-charset/libcharset.a \
++	../lib/liblib.a \
++	$(unused_objects) 
++
++managesieve_LDADD = \
++	$(libs) \
++	$(LIBICONV) \
++	$(RAND_LIBS) \
++	$(MODULE_LIBS) 
++
++managesieve_DEPENDENCIES = $(libs)
++
++cmds = \
++	cmd-capability.c \
++	cmd-logout.c \
++	cmd-putscript.c \
++	cmd-getscript.c \
++	cmd-setactive.c \
++	cmd-deletescript.c \
++	cmd-listscripts.c \
++	cmd-havespace.c 
++
++managesieve_SOURCES = \
++	$(cmds) \
++	client.c \
++	commands.c \
++	main.c 
++
++noinst_HEADERS = \
++	client.h \
++	commands.h \
++	common.h 
+diff -r 894f003d9f5f src/managesieve/client.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/managesieve/client.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,541 @@
++#include "common.h"
++#include "str.h"
++#include "ioloop.h"
++#include "network.h"
++#include "istream.h"
++#include "ostream.h"
++#include "commands.h"
++
++#include "managesieve-quote.h"
++#include "sieve-storage.h"
++#include "sieve-implementation.h"
++
++#include "client.h"
++
++#include <stdlib.h>
++#include <unistd.h>
++
++static struct client *my_client; /* we don't need more than one currently */
++static struct timeout *to_idle;
++
++struct client *client_create(int fd_in, int fd_out, struct sieve_storage *storage)
++{
++	struct client *client;
++
++	/* always use nonblocking I/O */
++	net_set_nonblock(fd_in, TRUE);
++	net_set_nonblock(fd_out, TRUE);
++
++	client = i_new(struct client, 1);
++	client->fd_in = fd_in;
++	client->fd_out = fd_out;
++	client->input = i_stream_create_file(fd_in, default_pool,
++					     managesieve_max_line_length, FALSE);
++	client->output = o_stream_create_file(fd_out, default_pool,
++					      (size_t)-1, FALSE);
++
++	o_stream_set_flush_callback(client->output, _client_output, client);
++
++	client->io = io_add(fd_in, IO_READ, _client_input, client);
++	client->parser = managesieve_parser_create(client->input, client->output,
++					    managesieve_max_line_length);
++	client->last_input = ioloop_time;
++
++	client->cmd.pool = pool_alloconly_create("command pool", 8192);
++	client->cmd.client = client;
++
++	client->storage = storage;
++
++	i_assert(my_client == NULL);
++	my_client = client;
++
++	if (hook_client_created != NULL)
++		hook_client_created(&client);
++	return client;
++}
++
++static const char *client_get_disconnect_reason(struct client *client)
++{
++	errno = client->input->stream_errno != 0 ?
++		client->input->stream_errno :
++		client->output->stream_errno;
++	return errno == 0 || errno == EPIPE ? "Connection closed" :
++		t_strdup_printf("Connection closed: %m");
++}
++
++void client_destroy(struct client *client, const char *reason)
++{
++	int ret;
++
++ 	i_assert(!client->handling_input);
++	i_assert(!client->destroyed);
++	client->destroyed = TRUE;
++
++	if (!client->disconnected) {
++		client->disconnected = TRUE;
++		if (reason == NULL)
++			reason = client_get_disconnect_reason(client);
++		i_info("%s", reason);
++	}
++
++	if (client->command_pending) {
++		/* try to deinitialize the command */
++		i_assert(client->cmd.func != NULL);
++		i_stream_close(client->input);
++		o_stream_close(client->output);
++		client->input_pending = FALSE;
++
++		ret = client->cmd.func(&client->cmd);
++		i_assert(ret);
++	}
++
++	managesieve_parser_destroy(&client->parser);
++	if (client->io != NULL)
++		io_remove(&client->io);
++
++	i_stream_destroy(&client->input);
++	o_stream_destroy(&client->output);
++
++	if (close(client->fd_in) < 0)
++		i_error("close(client in) failed: %m");
++	if (client->fd_in != client->fd_out) {
++		if (close(client->fd_out) < 0)
++			i_error("close(client out) failed: %m");
++	}
++
++	pool_unref(client->cmd.pool);
++	i_free(client);
++
++	/* quit the program */
++	my_client = NULL;
++	io_loop_stop(ioloop);
++}
++
++void client_disconnect(struct client *client, const char *reason)
++{
++	i_assert(reason != NULL);
++
++	if (client->disconnected)
++		return;
++
++	i_info("Disconnected: %s", reason);
++	client->disconnected = TRUE;
++	(void)o_stream_flush(client->output);
++
++	i_stream_close(client->input);
++	o_stream_close(client->output);
++}
++
++void client_disconnect_with_error(struct client *client, const char *msg)
++{
++	client_send_bye(client, msg);
++	client_disconnect(client, msg);
++}
++
++int client_send_line(struct client *client, const char *data) 
++{
++	struct const_iovec iov[2];
++
++	if (client->output->closed)
++		return -1;
++
++	iov[0].iov_base = data;
++	iov[0].iov_len = strlen(data);
++	iov[1].iov_base = "\r\n";
++	iov[1].iov_len = 2;
++
++	if (o_stream_sendv(client->output, iov, 2) < 0)
++		return -1;
++	client->last_output = ioloop_time;
++
++	if (o_stream_get_buffer_used_size(client->output) >=
++	    CLIENT_OUTPUT_OPTIMAL_SIZE) {
++		/* buffer full, try flushing */
++		return o_stream_flush(client->output);
++	}
++	return 1;
++}
++
++void client_send_response(struct client *client,
++  const char *oknobye, const char *resp_code, const char *msg)
++{
++	string_t *str;
++	
++	str = t_str_new(128);
++	str_append(str, oknobye);
++
++	if ( resp_code != NULL ) {
++		str_append(str, " (");
++		str_append(str, resp_code);
++		str_append_c(str, ')');
++	}
++
++	if ( msg != NULL ) {
++		str_append_c(str, ' ');
++		managesieve_quote_append_string(str, msg, TRUE);
++	}
++
++	client_send_line(client, str_c(str));
++}
++
++void client_send_command_error(struct client_command_context *cmd,
++			       const char *msg)
++{
++	struct client *client = cmd->client;
++	const char *error, *cmd_name;
++	bool fatal;
++
++	if (msg == NULL) {
++		msg = managesieve_parser_get_error(client->parser, &fatal);
++		if (fatal) {
++			client_disconnect_with_error(client, msg);
++			return;
++		}
++	}
++
++	if (cmd->name == NULL)
++		error = t_strconcat
++			("Error in MANAGESIEVE command: ", msg, NULL);
++	else {
++		cmd_name = t_str_ucase(cmd->name);
++		error = t_strconcat
++			("Error in MANAGESIEVE command ", cmd_name, ": ", msg, NULL);
++	}
++
++	client_send_no(client, error);
++
++	if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
++		client_disconnect_with_error(client,
++			"Too many invalid MANAGESIEVE commands.");
++	}
++
++	/* client_read_args() failures rely on this being set, so that the
++	   command processing is stopped even while command function returns
++	   FALSE. */
++	cmd->param_error = TRUE;
++}
++
++void client_send_storage_error(struct client *client,
++             struct sieve_storage *storage)
++{
++  const char *error;
++
++  error = sieve_storage_get_last_error(storage);
++
++  client_send_no(client, error);
++}
++
++void client_send_sieve_error(struct client *client)
++{
++  const char *error;
++
++  error = sieve_get_last_error();
++
++  client_send_no(client, error);
++}
++
++bool client_read_args(struct client_command_context *cmd, unsigned int count,
++		      unsigned int flags, struct managesieve_arg **args)
++{
++	int ret;
++
++	i_assert(count <= INT_MAX);
++
++	ret = managesieve_parser_read_args(cmd->client->parser, count, flags, args);
++	if (ret >= (int)count) {
++		/* all parameters read successfully */
++		return TRUE;
++	} else if (ret == -2) {
++		/* need more data */
++		return FALSE;
++	} else {
++		/* error, or missing arguments */
++		client_send_command_error(cmd, ret < 0 ? NULL :
++					  "Missing arguments");
++		return FALSE;
++	}
++}
++
++bool client_read_string_args(struct client_command_context *cmd,
++			     unsigned int count, ...)
++{
++	struct managesieve_arg *managesieve_args;
++	va_list va;
++	const char *str;
++	unsigned int i;
++
++	if (!client_read_args(cmd, count, 0, &managesieve_args))
++		return FALSE;
++
++	va_start(va, count);
++	for (i = 0; i < count; i++) {
++		const char **ret = va_arg(va, const char **);
++
++		if (managesieve_args[i].type == MANAGESIEVE_ARG_EOL) {
++			client_send_command_error(cmd, "Missing arguments.");
++			break;
++		}
++
++		str = managesieve_arg_string(&managesieve_args[i]);
++		if (str == NULL) {
++			client_send_command_error(cmd, "Invalid arguments.");
++			break;
++		}
++
++		if (ret != NULL)
++			*ret = str;
++	}
++	va_end(va);
++
++	return i == count;
++}
++
++void _client_reset_command(struct client *client)
++{
++	pool_t pool;
++	size_t size;
++
++	/* reset input idle time because command output might have taken a
++	   long time and we don't want to disconnect client immediately then */
++	client->last_input = ioloop_time;
++
++	client->command_pending = FALSE;
++    if (client->io == NULL && !client->disconnected) {
++        i_assert(i_stream_get_fd(client->input) >= 0);
++        client->io = io_add(i_stream_get_fd(client->input),
++                    IO_READ, _client_input, client);
++    }
++    o_stream_set_flush_callback(client->output, _client_output, client);
++
++	pool = client->cmd.pool;
++	memset(&client->cmd, 0, sizeof(client->cmd));
++
++	p_clear(pool);
++	client->cmd.pool = pool;
++	client->cmd.client = client;
++
++	managesieve_parser_reset(client->parser);
++
++	/* if there's unread data in buffer, remember that there's input
++	   pending and we should get around to calling client_input() soon.
++	   This is mostly for APPEND/IDLE. */
++	(void)i_stream_get_data(client->input, &size);
++	if (size > 0 && !client->destroyed)
++		client->input_pending = TRUE;
++}
++
++/* Skip incoming data until newline is found,
++   returns TRUE if newline was found. */
++static bool client_skip_line(struct client *client)
++{
++	const unsigned char *data;
++	size_t i, data_size;
++
++	data = i_stream_get_data(client->input, &data_size);
++
++	for (i = 0; i < data_size; i++) {
++		if (data[i] == '\n') {
++			client->input_skip_line = FALSE;
++			i++;
++			break;
++		}
++	}
++
++	i_stream_skip(client->input, i);
++	return !client->input_skip_line;
++}
++
++static bool client_handle_input(struct client_command_context *cmd)
++{
++	struct client *client = cmd->client;
++
++	if (cmd->func != NULL) {
++		/* command is being executed - continue it */
++		if (cmd->func(cmd) || cmd->param_error) {
++			/* command execution was finished */
++			if (!cmd->param_error)
++				client->bad_counter = 0;
++			_client_reset_command(client);
++			return TRUE;
++		}
++
++		/* unfinished */
++        if (client->command_pending)
++            o_stream_set_flush_pending(client->output, TRUE);
++		return FALSE;
++	}
++
++	if (client->input_skip_line) {
++		/* we're just waiting for new line.. */
++		if (!client_skip_line(client))
++			return FALSE;
++
++		/* got the newline */
++		_client_reset_command(client);
++
++		/* pass through to parse next command */
++	}
++
++	if (cmd->name == NULL) {
++		cmd->name = managesieve_parser_read_word(client->parser);
++		if (cmd->name == NULL)
++			return FALSE; /* need more data */
++		cmd->name = p_strdup(cmd->pool, cmd->name);
++	}
++
++	if (cmd->name == '\0') {
++		/* command not given - cmd_func is already NULL. */
++	} else {
++		/* find the command function */
++		cmd->func = command_find(cmd->name);
++	}
++
++	client->input_skip_line = TRUE;
++	if (cmd->func == NULL) {
++		/* unknown command */
++		client_send_command_error(cmd, "Unknown command.");
++		_client_reset_command(client);
++	} else {
++		i_assert(!client->disconnected);
++
++		client_handle_input(cmd);
++	}
++
++	return TRUE;
++}
++
++void _client_input(void *context)
++{
++	struct client *client = context;
++	struct client_command_context *cmd = &client->cmd;
++	int ret;
++
++	if (client->command_pending) {
++		/* already processing one command. wait. */
++		io_remove(&client->io);
++		return;
++	}
++
++	client->input_pending = FALSE;
++	client->last_input = ioloop_time;
++
++	switch (i_stream_read(client->input)) {
++	case -1:
++		/* disconnected */
++		client_destroy(client, NULL);
++		return;
++	case -2:
++		/* parameter word is longer than max. input buffer size.
++		   this is most likely an error, so skip the new data
++		   until newline is found. */
++		client->input_skip_line = TRUE;
++
++		client_send_command_error(cmd, "Too long argument.");
++		_client_reset_command(client);
++		break;
++	}
++
++	client->handling_input = TRUE;
++	o_stream_cork(client->output);
++	do {
++		t_push();
++		ret = client_handle_input(cmd);
++		t_pop();
++	} while (ret && !client->disconnected);
++    o_stream_uncork(client->output);
++    client->handling_input = FALSE;
++
++	if (client->command_pending)
++		client->input_pending = TRUE;
++
++	if (client->output->closed)
++		client_destroy(client, NULL);
++}
++
++int _client_output(void *context)
++{
++	struct client *client = context;
++	struct client_command_context *cmd = &client->cmd;
++	int ret;
++	bool finished;
++
++	client->last_output = ioloop_time;
++
++	if ((ret = o_stream_flush(client->output)) < 0) {
++		client_destroy(client, NULL);
++		return 1;
++	}
++
++	if (!client->command_pending)
++		return 1;
++
++	/* continue processing command */
++	o_stream_cork(client->output);
++	client->output_pending = TRUE;
++	finished = cmd->func(cmd) || cmd->param_error;
++
++	/* a bit kludgy check. normally we would want to get back to this
++	   output handler, but IDLE is a special case which has command
++	   pending but without necessarily anything to write. */
++	if (!finished && client->output_pending)
++		o_stream_set_flush_pending(client->output, TRUE);
++
++	o_stream_uncork(client->output);
++
++	if (finished) {
++		/* command execution was finished */
++		client->bad_counter = 0;
++		_client_reset_command(client);
++
++		if (client->input_pending)
++			_client_input(client);
++	}
++	return ret;
++}
++
++static void idle_timeout(void *context __attr_unused__)
++{
++	time_t idle_time, last_change;
++
++	if (my_client == NULL)
++		return;
++
++	/* We mostly want to check last_input here, but if there is a very long
++	   running command (like copying thousands of messages), we don't want
++	   to disconnect the client just after the command was finished.
++	   But any output that IDLE has sent should be ignored. */
++	last_change = I_MAX(my_client->last_input, my_client->last_output);
++	idle_time = ioloop_time - last_change;
++
++	if (my_client->command_pending &&
++		o_stream_get_buffer_used_size(my_client->output) > 0 &&
++		idle_time >= CLIENT_OUTPUT_TIMEOUT) {
++        /* client isn't reading our output */
++		client_destroy(my_client, "Disconnected for inactivity "
++                   "in reading our output");
++	} else if (idle_time >= CLIENT_IDLE_TIMEOUT) {
++		/* client isn't sending us anything */
++		if (!my_client->command_pending) {
++			client_send_bye(my_client,
++				"Disconnected for inactivity.");
++		}
++		client_destroy(my_client, "Disconnected for inactivity");
++	}
++}
++
++void clients_init(void)
++{
++	my_client = NULL;
++	to_idle = timeout_add(10000, idle_timeout, NULL);
++}
++
++void clients_deinit(void)
++{
++	if (my_client != NULL) {
++		client_send_bye(my_client, "Server shutting down.");
++		client_destroy(my_client, "Server shutting down");
++	}
++
++	timeout_remove(&to_idle);
++}
++
+diff -r 894f003d9f5f src/managesieve/client.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/managesieve/client.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,103 @@
++#ifndef __CLIENT_H
++#define __CLIENT_H
++
++#include "commands.h"
++
++struct client;
++struct sieve_storage;
++struct managesieve_parser;
++struct managesieve_arg;
++
++struct client_command_context {
++	struct client *client;
++
++	pool_t pool;
++	const char *name;
++
++	command_func_t *func;
++	void *context;
++
++	unsigned int param_error:1;
++};
++
++struct client {
++	int fd_in, fd_out;
++	struct sieve_storage *storage;
++
++	struct io *io;
++	struct istream *input;
++	struct ostream *output;
++
++	time_t last_input, last_output;
++	unsigned int bad_counter;
++
++	struct managesieve_parser *parser;
++	struct client_command_context cmd;
++
++	unsigned int disconnected:1;
++	unsigned int destroyed:1;
++	unsigned int command_pending:1;
++	unsigned int input_pending:1;
++	unsigned int output_pending:1;
++	unsigned int handling_input:1;
++	unsigned int rawlog:1;
++	unsigned int input_skip_line:1; /* skip all the data until we've
++					   found a new line */
++};
++
++/* Create new client with specified input/output handles. socket specifies
++   if the handle is a socket. */
++struct client *client_create(int fd_in, int fd_out, struct sieve_storage *storage);
++void client_destroy(struct client *client, const char *reason);
++
++/* Disconnect client connection */
++void client_disconnect(struct client *client, const char *reason);
++void client_disconnect_with_error(struct client *client, const char *msg);
++
++/* Send a line of data to client. Returns 1 if ok, 0 if buffer is getting full,
++   -1 if error */
++int client_send_line(struct client *client, const char *data);
++
++void client_send_response(struct client *client,
++  const char *oknobye, const char *resp_code, const char *msg);
++
++#define client_send_ok(client, msg) \
++  client_send_response(client, "OK", NULL, msg)
++#define client_send_no(client, msg) \
++  client_send_response(client, "NO", NULL, msg)
++#define client_send_bye(client, msg) \
++  client_send_response(client, "BYE", NULL, msg)
++
++#define client_send_okresp(client, resp_code, msg) \
++  client_send_response(client, "OK", resp_code, msg)
++#define client_send_noresp(client, resp_code, msg) \
++  client_send_response(client, "NO", resp_code, msg)
++#define client_send_byeresp(cmd, resp_code, msg) \
++  client_send_response(client, "BYE", resp_code, msg)
++
++/* Send BAD command error to client. msg can be NULL. */
++void client_send_command_error(struct client_command_context *cmd,
++			       const char *msg);
++
++/* Send storage or sieve related errors to the client */
++void client_send_storage_error(struct client *client,
++             struct sieve_storage *storage);
++void client_send_sieve_error(struct client *client);
++
++/* Read a number of arguments. Returns TRUE if everything was read or
++   FALSE if either needs more data or error occurred. */
++bool client_read_args(struct client_command_context *cmd, unsigned int count,
++		      unsigned int flags, struct managesieve_arg **args);
++/* Reads a number of string arguments. ... is a list of pointers where to
++   store the arguments. */
++bool client_read_string_args(struct client_command_context *cmd,
++			     unsigned int count, ...);
++
++void clients_init(void);
++void clients_deinit(void);
++
++void _client_reset_command(struct client *client);
++void _client_input(void *context);
++int _client_output(void *context);
++
++#endif
+diff -r 894f003d9f5f src/managesieve/cmd-capability.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/managesieve/cmd-capability.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,30 @@
++#include "common.h"
++#include "commands.h"
++#include "str.h"
++#include "strfuncs.h"
++#include "ostream.h"
++#include "sieve-implementation.h"
++
++bool cmd_capability(struct client_command_context *cmd)
++{
++	struct client *client = cmd->client;
++	const char *sievecap, *sieveimpl;
++
++	sievecap = sieve_get_capabilities();
++	if (sievecap == NULL)
++		sievecap = "";
++
++	t_push();		
++	sievecap = t_strconcat("\"SIEVE\" \"", sievecap, "\"", NULL);
++  	sieveimpl = t_strconcat("\"IMPLEMENTATION\" \"", 
++    managesieve_implementation_string, "\"", NULL);
++
++	client_send_line(client, sieveimpl);
++	client_send_line(client, sievecap);
++	client_send_line(client, "OK \"Capability completed.\"");
++	t_pop();
++
++	return TRUE;
++
++}
++
+diff -r 894f003d9f5f src/managesieve/cmd-deletescript.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/managesieve/cmd-deletescript.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,40 @@
++#include "common.h"
++#include "commands.h"
++
++#include "sieve-script.h"
++
++bool cmd_deletescript(struct client_command_context *cmd)
++{
++	struct client *client = cmd->client;
++	struct sieve_storage *storage = client->storage;
++	const char *scriptname;
++	struct sieve_script *script;
++	bool exists;
++
++	/* <scrip name>*/
++	if (!client_read_string_args(cmd, 1, &scriptname))
++		return FALSE;
++
++	exists = TRUE;
++	script = sieve_script_init(storage, scriptname, &exists);
++
++	if (script == NULL) {
++		if (!exists) 
++			client_send_no(client, "Script does not exist.");
++		else 
++			client_send_storage_error(client, storage);
++
++		return TRUE;
++	}
++
++	if (sieve_script_delete(&script) < 0)
++		client_send_storage_error(client, storage);
++	else
++		client_send_ok(client, "Deletescript completed.");
++
++	/* Script object is deleted no matter what in 
++	 * sieve_script_delete()
++	 */
++
++	return TRUE;
++}
+diff -r 894f003d9f5f src/managesieve/cmd-getscript.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/managesieve/cmd-getscript.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,122 @@
++#include "common.h"
++#include "ostream.h"
++#include "commands.h"
++#include "istream.h"
++#include "sieve-script.h"
++
++struct cmd_getscript_context {
++	struct client *client;
++	struct client_command_context *cmd;
++	struct sieve_storage *storage;	
++	uoff_t scriptsize;
++
++	struct sieve_script *script;
++	struct istream *scriptstream;
++	bool failed;
++	bool exists;
++};
++
++static bool cmd_getscript_finish(struct cmd_getscript_context *ctx)
++{
++  struct client *client = ctx->client;
++
++	if (ctx->script != NULL)
++		sieve_script_unref(&ctx->script);
++
++	if (ctx->failed) {
++		if (client->output->closed) {
++			client_disconnect(client, "Disconnected");
++			return TRUE;
++		}
++
++		if (!ctx->exists) {
++			client_send_no(client, "Script does not exist.");
++			return TRUE;
++		}
++		
++		client_send_storage_error(client, client->storage);
++		return TRUE;
++	}
++
++	client_send_line(client, "");
++	client_send_ok(client, "Getscript completed.");
++	return TRUE;
++}
++
++static bool cmd_getscript_continue(struct client_command_context *cmd)
++{
++	struct client *client = cmd->client;
++	struct cmd_getscript_context *ctx = cmd->context;
++
++	if (o_stream_send_istream(client->output, ctx->scriptstream) < 0) {
++		sieve_storage_set_critical(ctx->storage,
++			"o_stream_send_istream(%s) failed: %m", sieve_script_name(ctx->script));
++		ctx->failed = TRUE;    
++	}
++
++	/* FIXME: Check whether there is a bug in the io_stream_sendfile function
++	 * as the eof indicator of the input stream is never set. The stream_sendfile
++	 * function does not use read functions of the inputstream and therefore
++	 * the eof indicator will not be updated. Workaround: check v_offset == size 
++	 * as well.
++	 */
++	if (ctx->scriptstream->eof || ctx->scriptstream->closed ||
++		ctx->scriptstream->v_offset == ctx->scriptsize ) {
++		if (client->output->closed || ctx->scriptstream->v_offset < ctx->scriptsize) 
++			ctx->failed = TRUE;
++	} else if (!ctx->failed) 
++		/* unfinished */
++		return FALSE;
++
++	return cmd_getscript_finish(ctx);
++}
++
++bool cmd_getscript(struct client_command_context *cmd)
++{
++	struct client *client = cmd->client;
++	struct cmd_getscript_context *ctx;
++	const char *scriptname;
++	int ret;
++	bool deleted_r;
++
++	/* <scriptname> */
++	if (!client_read_string_args(cmd, 1, &scriptname))
++		return FALSE;
++
++	ctx = p_new(cmd->pool, struct cmd_getscript_context, 1);
++	ctx->cmd = cmd;
++	ctx->client = client;
++	ctx->storage = client->storage;
++	ctx->failed = FALSE;
++	
++	ctx->exists = TRUE;
++	ctx->script = sieve_script_init(client->storage, scriptname, &ctx->exists);
++
++	if (ctx->script == NULL) {
++		ctx->failed = TRUE;
++		return cmd_getscript_finish(ctx);
++	}
++			
++	ctx->scriptstream = sieve_script_open(ctx->script, &deleted_r);
++
++	if ( ctx->scriptstream == NULL ) {
++		ctx->failed = TRUE;
++		ctx->exists = !deleted_r;
++		return cmd_getscript_finish(ctx);
++	}
++
++	ctx->scriptsize = 0;
++	if ( (ret=sieve_script_get_size(ctx->script, &ctx->scriptsize)) <= 0 ) {
++		ctx->failed = TRUE;
++		ctx->exists = (ret < 0);
++		return cmd_getscript_finish(ctx);
++	}
++
++	client_send_line(client, t_strdup_printf("{%"PRIuUOFF_T"}", ctx->scriptsize));
++
++	client->command_pending = TRUE;
++	cmd->func = cmd_getscript_continue;
++	cmd->context = ctx;
++
++	return cmd_getscript_continue(cmd);
++}
+diff -r 894f003d9f5f src/managesieve/cmd-havespace.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/managesieve/cmd-havespace.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,38 @@
++#include "common.h"
++#include "commands.h"
++
++bool cmd_havespace(struct client_command_context *cmd)
++{
++  struct client *client = cmd->client;
++	struct managesieve_arg *args;
++	const char *scriptname;
++	uoff_t size;
++	int ret;
++
++	/* <scriptname> <size> */
++	if (!(ret=client_read_args(cmd, 2, 0, &args)))
++	  return FALSE;
++
++	if ( ret > 2 ) {
++		client_send_no(client, "Too many arguments");
++		return TRUE;
++	}
++
++	if ( (scriptname = managesieve_arg_string(&args[0])) == NULL ) {
++		client_send_no(client, "Invalid string for scriptname.");
++		return TRUE;
++	}
++
++	if ( managesieve_arg_number(&args[1], &size) < 0 ) {
++		client_send_no(client, "Invalid scriptsize argument.");
++		return TRUE;
++	}
++
++	if ( size == 0 ) {
++		client_send_no(client, "Cannot upload empty script.");
++		return TRUE;
++	}
++
++	client_send_ok(client, "Putscript would succeed.");
++	return TRUE;
++}
+diff -r 894f003d9f5f src/managesieve/cmd-listscripts.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/managesieve/cmd-listscripts.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,46 @@
++#include "common.h"
++#include "commands.h"
++#include "str.h"
++#include "sieve-storage.h"
++#include "sieve-list.h"
++#include "managesieve-quote.h"
++
++bool cmd_listscripts(struct client_command_context *cmd)
++{
++  struct client *client = cmd->client;
++	struct sieve_list_context *ctx;
++	const char *scriptname;
++	bool active;
++	string_t *str;
++
++	if ( (ctx = sieve_storage_list_init(client->storage))
++		== NULL ) {
++		client_send_storage_error(client, client->storage);
++		return TRUE;
++	}
++
++	/* FIXME: This will be quite slow for large script lists. Implement
++	 * some buffering to fix this. Wont truely be an issue with managesieve
++	 * though.
++	 */
++	while ((scriptname = sieve_storage_list_next(ctx, &active)) != NULL) {
++		t_push();
++		str = t_str_new(128);
++	  
++		managesieve_quote_append_string(str, scriptname, FALSE);
++		
++		if ( active ) 
++		  str_append(str, " ACTIVE");
++		
++		client_send_line(client, str_c(str));
++		t_pop();
++	}
++  
++	if ( sieve_storage_list_deinit(&ctx) < 0 ) {
++		client_send_storage_error(client, client->storage);
++		return TRUE;
++	}
++	
++	client_send_ok(client, "Listscripts completed.");
++	return TRUE;
++}
+diff -r 894f003d9f5f src/managesieve/cmd-logout.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/managesieve/cmd-logout.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,12 @@
++#include "common.h"
++#include "ostream.h"
++#include "commands.h"
++
++bool cmd_logout(struct client_command_context *cmd)
++{
++	struct client *client = cmd->client;
++
++	client_send_line(client, "OK \"Logout completed.\"");
++	client_disconnect(client, "Logged out");
++	return TRUE;
++}
+diff -r 894f003d9f5f src/managesieve/cmd-putscript.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/managesieve/cmd-putscript.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,355 @@
++#include "common.h"
++#include "ioloop.h"
++#include "istream.h"
++#include "ostream.h"
++#include "str.h"
++#include "commands.h"
++#include "managesieve-parser.h"
++#include "sieve-storage.h"
++#include "sieve-save.h"
++#include "sieve-implementation.h"
++
++#include <sys/time.h>
++
++struct cmd_putscript_context {
++	struct client *client;
++	struct client_command_context *cmd;
++	struct sieve_storage *storage;
++
++	struct istream *input;
++
++	const char *scriptname;
++	uoff_t script_size;	
++
++	struct managesieve_parser *save_parser;
++	struct sieve_save_context *save_ctx;
++};
++
++static void cmd_putscript_finish(struct cmd_putscript_context *ctx);
++static bool cmd_putscript_continue_script(struct client_command_context *cmd);
++
++static void client_input(void *context)
++{
++	struct client *client = context;
++	struct client_command_context *cmd = &client->cmd;
++
++	client->last_input = ioloop_time;
++
++	switch (i_stream_read(client->input)) {
++	case -1:
++		/* disconnected */
++		cmd_putscript_finish(cmd->context);
++		/* Reset command so that client_destroy() doesn't try to call
++		   cmd_putscript_continue_script() anymore. */
++		_client_reset_command(client);
++		client_destroy(client, "Disconnected in putscript");
++		return;
++	case -2:
++		cmd_putscript_finish(cmd->context);
++		if (client->command_pending) {
++			/* message data, this is handled internally by
++			   mailbox_save_continue() */
++			break;
++		}
++
++		/* parameter word is longer than max. input buffer size.
++		   this is most likely an error, so skip the new data
++		   until newline is found. */
++		client->input_skip_line = TRUE;
++
++		client_send_command_error(cmd, "Too long argument.");
++		_client_reset_command(client);
++		return;
++	}
++
++	if (cmd->func(cmd)) {
++		/* command execution was finished. Note that if cmd_sync()
++		   didn't finish, we didn't get here but the input handler
++		   has already been moved. So don't do anything important
++		   here..
++
++		   reset command once again to reset cmd_sync()'s changes. */
++		_client_reset_command(client);
++
++		if (client->input_pending)
++			_client_input(client);
++	}
++}
++
++static void cmd_putscript_finish(struct cmd_putscript_context *ctx)
++{
++	io_remove(&ctx->client->io);
++
++	managesieve_parser_destroy(&ctx->save_parser);
++
++	if (ctx->input != NULL)
++		i_stream_unref(&ctx->input);
++
++	if (ctx->save_ctx != NULL)
++	{
++		ctx->client->input_skip_line = TRUE;
++		sieve_storage_save_abort(ctx->save_ctx);
++	}
++
++	ctx->client->bad_counter = 0;
++	o_stream_set_flush_callback(ctx->client->output,
++				    _client_output, ctx->client);
++}
++
++static bool cmd_putscript_continue_cancel(struct client_command_context *cmd)
++{
++	struct cmd_putscript_context *ctx = cmd->context;
++	size_t size;
++
++	(void)i_stream_read(ctx->input);
++	(void)i_stream_get_data(ctx->input, &size);
++	i_stream_skip(ctx->input, size);
++
++	if (ctx->input->v_offset == ctx->script_size ||
++	    cmd->client->input->closed) {
++		cmd_putscript_finish(ctx);
++		return TRUE;
++	}
++	return FALSE;
++}
++
++static bool cmd_putscript_cancel(struct cmd_putscript_context *ctx, bool nonsync)
++{
++	ctx->client->input_skip_line = TRUE;
++
++	if (!nonsync) {
++		cmd_putscript_finish(ctx);
++		return TRUE;
++	}
++
++	/* we have to read the nonsynced literal so we don't treat the message
++	   data as commands. */
++	ctx->input = i_stream_create_limit(default_pool, ctx->client->input,
++					   ctx->client->input->v_offset,
++					   ctx->script_size);
++
++	ctx->client->command_pending = TRUE;
++	ctx->cmd->func = cmd_putscript_continue_cancel;
++	ctx->cmd->context = ctx;
++	return cmd_putscript_continue_cancel(ctx->cmd);
++}
++
++static bool cmd_putscript_finish_parsing(struct client_command_context *cmd)
++{
++	struct client *client = cmd->client;
++	struct cmd_putscript_context *ctx = cmd->context;
++	struct managesieve_arg *args;
++	struct sieve_script *script;
++	int ret;
++	
++	/* if error occurs, the CRLF is already read. */
++	client->input_skip_line = FALSE;
++	
++	/* <script literal> */
++	ret = managesieve_parser_read_args(ctx->save_parser, 0,
++          MANAGESIEVE_PARSE_FLAG_LITERAL_SIZE, &args);
++
++	if (ret == -1) {
++		if (ctx->storage != NULL)
++			client_send_command_error(cmd, NULL);
++		cmd_putscript_finish(ctx);
++		return TRUE;
++	}
++	if (ret < 0) {
++		/* need more data */
++		return FALSE;
++	}
++
++	if (args[0].type == MANAGESIEVE_ARG_EOL) {
++		/* last message */
++	  /* eat away the trailing CRLF */
++		client->input_skip_line = TRUE;
++
++		script = sieve_storage_save_get_tempscript
++	  	(ctx->save_ctx);
++
++		if ( script == NULL || (sieve_compile(script, TRUE) < 0)) {
++			client_send_sieve_error(client);
++			cmd_putscript_finish(ctx);
++			return TRUE;
++		} else {
++			ret = sieve_storage_save_commit(ctx->save_ctx);
++			if (ret < 0) {
++				client_send_storage_error(client, ctx->storage);
++				cmd_putscript_finish(ctx);
++				return TRUE;
++			} else 
++				ctx->save_ctx = NULL;
++		}
++	  
++		cmd_putscript_finish(ctx);
++		client_send_ok(client, "Putscript completed.");
++		
++		return TRUE;
++	}
++
++	client_send_command_error(cmd, "Too many command arguments.");
++	cmd_putscript_finish(ctx);
++	return TRUE;
++}
++
++static bool cmd_putscript_continue_parsing(struct client_command_context *cmd)
++{
++	struct client *client = cmd->client;
++	struct cmd_putscript_context *ctx = cmd->context;
++	struct managesieve_arg *args;
++	bool nonsync = FALSE;
++	int ret;
++
++	/* if error occurs, the CRLF is already read. */
++	client->input_skip_line = FALSE;
++
++	/* <script literal> */
++	ret = managesieve_parser_read_args(ctx->save_parser, 0,
++				    MANAGESIEVE_PARSE_FLAG_LITERAL_SIZE, &args);
++	if (ret == -1) {
++		cmd_putscript_finish(ctx);
++		client_send_command_error(cmd, "Invalid arguments.");
++		client->input_skip_line = TRUE;
++		return TRUE;
++	}
++	if (ret < 0) {
++		/* need more data */
++		return FALSE;
++	}
++
++	if (args->type != MANAGESIEVE_ARG_STRING) {
++		/* Validate the script argument */
++	  	if (args->type != MANAGESIEVE_ARG_LITERAL_SIZE ) {
++			client_send_command_error(cmd, "Invalid arguments.");
++			return cmd_putscript_cancel(ctx, FALSE);
++		}
++
++		ctx->script_size = MANAGESIEVE_ARG_LITERAL_SIZE(args);
++		nonsync = TRUE;
++	} else {
++	  	/* FIXME */
++		client_send_no(client, "This MANAGESIEVE implementation currently does not allow "
++			       "quoted strings to be used for script contents.");
++		return cmd_putscript_cancel(ctx, FALSE);		
++	}
++
++	if (ctx->script_size == 0) {
++		/* no message data, abort */
++		client_send_no(client, "PUTSCRIPT aborted (no message data).");
++		cmd_putscript_finish(ctx);
++		return TRUE;
++	}
++
++	/* save the script */
++	ctx->input = i_stream_create_limit(default_pool, client->input,
++					   client->input->v_offset,
++					   ctx->script_size);
++	ctx->save_ctx = sieve_storage_save_init(ctx->storage, ctx->scriptname, ctx->input);
++
++	if ( ctx->save_ctx == NULL ) {
++		/* save initialization failed */
++		client_send_storage_error(client, ctx->storage);
++		return cmd_putscript_cancel(ctx, nonsync);
++	}
++
++	/* after literal comes CRLF, if we fail make sure we eat it away */
++	client->input_skip_line = TRUE;
++
++	client->command_pending = TRUE;
++	cmd->func = cmd_putscript_continue_script;
++	return cmd_putscript_continue_script(cmd);
++}
++
++static bool cmd_putscript_continue_script(struct client_command_context *cmd)
++{
++	struct client *client = cmd->client;
++	struct cmd_putscript_context *ctx = cmd->context;
++	size_t size;
++	bool failed;
++
++	if (ctx->save_ctx != NULL) {
++		if (sieve_storage_save_continue(ctx->save_ctx) < 0) {
++			/* we still have to finish reading the script
++			   from client */
++			sieve_storage_save_abort(ctx->save_ctx);
++			ctx->save_ctx = NULL;
++		}
++	}
++
++	if (ctx->save_ctx == NULL) {
++		(void)i_stream_read(ctx->input);
++		(void)i_stream_get_data(ctx->input, &size);
++		i_stream_skip(ctx->input, size);
++	}
++
++	if (ctx->input->eof || client->input->closed) {
++		/* finished */
++		bool all_written = ctx->input->v_offset == ctx->script_size;
++
++		i_stream_unref(&ctx->input);
++		ctx->input = NULL;
++
++		if (ctx->save_ctx == NULL) {
++			/* failed above */
++			client_send_storage_error(client, ctx->storage);
++			failed = TRUE;
++		} else if (!all_written) {
++			/* client disconnected before it finished sending the
++			   whole message. */
++			failed = TRUE;
++			sieve_storage_save_abort(ctx->save_ctx);
++		} else if (sieve_storage_save_finish(ctx->save_ctx) < 0) {
++			failed = TRUE;
++			client_send_storage_error(client, ctx->storage);
++		} else {
++			failed = client->input->closed;
++		}
++
++		if (failed) {
++			cmd_putscript_finish(ctx);
++			return TRUE;
++		}
++
++		/* prepare for next message */
++		client->command_pending = FALSE;
++		managesieve_parser_reset(ctx->save_parser);
++		cmd->func = cmd_putscript_finish_parsing;
++		return cmd_putscript_finish_parsing(cmd);
++	}
++
++	return FALSE;
++}
++
++bool cmd_putscript(struct client_command_context *cmd)
++{
++	struct client *client = cmd->client;
++	struct cmd_putscript_context *ctx;
++	const char *scriptname;
++
++	/* <scriptname> */
++	if (!client_read_string_args(cmd, 1, &scriptname) || 
++		*scriptname == '\0')
++		return FALSE;
++
++	ctx = p_new(cmd->pool, struct cmd_putscript_context, 1);
++	ctx->cmd = cmd;
++	ctx->client = client;
++	ctx->storage = client->storage;
++	ctx->scriptname = scriptname;
++
++	io_remove(&client->io);
++	client->io = io_add(i_stream_get_fd(client->input), IO_READ,
++			    client_input, client);
++	/* putscript is special because we're only waiting on client input, not
++	   client output, so disable the standard output handler until we're
++	   finished */
++	o_stream_set_flush_callback(client->output, NULL, NULL);
++
++	ctx->save_parser = managesieve_parser_create(client->input, client->output,
++					      managesieve_max_line_length);
++
++	cmd->func = cmd_putscript_continue_parsing;
++	cmd->context = ctx;
++	return cmd_putscript_continue_parsing(cmd);
++}
+diff -r 894f003d9f5f src/managesieve/cmd-setactive.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/managesieve/cmd-setactive.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,53 @@
++#include "common.h"
++#include "commands.h"
++
++#include "sieve-script.h"
++
++bool cmd_setactive(struct client_command_context *cmd)
++{
++	struct client *client = cmd->client;
++	struct sieve_storage *storage = client->storage;
++	const char *scriptname;
++	struct sieve_script *script;
++	bool exists;
++	int ret;
++
++	/* <scriptname> */
++	if (!client_read_string_args(cmd, 1, &scriptname))
++		return FALSE;
++
++	if ( *scriptname != '\0' ) {
++		exists = TRUE;
++		script = sieve_script_init(storage, scriptname, &exists);
++
++		if (script == NULL) {
++			if (!exists)
++				client_send_no(client, "Script does not exist.");
++			else
++				client_send_storage_error(client, storage);
++
++			return TRUE;
++		}
++	
++		ret = sieve_script_activate(script);
++		if ( ret < 0 )
++			client_send_storage_error(client, storage);
++		else
++			client_send_ok(client, ret ? 
++				"Setactive completed." :
++				"Script is already active.");
++
++		sieve_script_unref(&script);
++	} else {
++		ret = sieve_storage_deactivate(storage);
++		
++		if ( ret < 0 )
++			client_send_storage_error(client, storage);
++		else
++			client_send_ok(client, ret ?
++ 				"Active script is now deactivated." :
++				"No scripts currently active.");	
++	}
++
++	return TRUE;
++}
+diff -r 894f003d9f5f src/managesieve/commands.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/managesieve/commands.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,112 @@
++#include "common.h"
++#include "array.h"
++#include "commands.h"
++
++#include <stdlib.h>
++
++/* Might want to combine this somewhere in a commands-common.c 
++ * to avoid duplicate code 
++ */
++
++const struct command managesieve_commands[] = {
++	{ "CAPABILITY", cmd_capability },
++	{ "LOGOUT", cmd_logout },
++	{ "PUTSCRIPT", cmd_putscript },
++	{ "GETSCRIPT", cmd_getscript },
++	{ "SETACTIVE", cmd_setactive },
++	{ "DELETESCRIPT", cmd_deletescript },
++	{ "LISTSCRIPTS", cmd_listscripts },
++	{ "HAVESPACE", cmd_havespace }
++};
++
++#define MANAGESIEVE_COMMANDS_COUNT \
++	(sizeof(managesieve_commands) / sizeof(managesieve_commands[0]))
++
++static array_t ARRAY_DEFINE(commands, struct command);
++static bool commands_unsorted;
++
++void command_register(const char *name, command_func_t *func)
++{
++	struct command cmd;
++
++	cmd.name = name;
++	cmd.func = func;
++	array_append(&commands, &cmd, 1);
++
++	commands_unsorted = TRUE;
++}
++
++void command_unregister(const char *name)
++{
++	const struct command *cmd;
++	unsigned int i, count;
++
++	cmd = array_get(&commands, &count);
++	for (i = 0; i < count; i++) {
++		if (strcasecmp(cmd[i].name, name) == 0) {
++			array_delete(&commands, i, 1);
++			return;
++		}
++	}
++
++	i_error("Trying to unregister unknown command '%s'", name);
++}
++
++void command_register_array(const struct command *cmdarr, unsigned int count)
++{
++	commands_unsorted = TRUE;
++	array_append(&commands, cmdarr, count);
++}
++
++void command_unregister_array(const struct command *cmdarr, unsigned int count)
++{
++	while (count > 0) {
++		command_unregister(cmdarr->name);
++		count--; cmdarr++;
++	}
++}
++
++static int command_cmp(const void *p1, const void *p2)
++{
++	const struct command *c1 = p1, *c2 = p2;
++
++	return strcasecmp(c1->name, c2->name);
++}
++
++static int command_bsearch(const void *name, const void *cmd_p)
++{
++	const struct command *cmd = cmd_p;
++
++	return strcasecmp(name, cmd->name);
++}
++
++command_func_t *command_find(const char *name)
++{
++	const struct command *cmd;
++	void *base;
++	unsigned int count;
++
++	base = array_get_modifyable(&commands, &count);
++	if (commands_unsorted) {
++		qsort(base, count, sizeof(struct command), command_cmp);
++			    commands_unsorted = FALSE;
++	}
++
++    cmd = bsearch(name, base, count, sizeof(struct command),
++		     command_bsearch);
++	return cmd == NULL ? NULL : cmd->func;
++}
++
++void commands_init(void)
++{
++  	ARRAY_CREATE(&commands, system_pool, struct command, 64);
++    commands_unsorted = FALSE;
++
++	command_register_array(managesieve_commands, MANAGESIEVE_COMMANDS_COUNT);
++}
++
++void commands_deinit(void)
++{
++	command_unregister_array(managesieve_commands, MANAGESIEVE_COMMANDS_COUNT);
++	array_free(&commands);
++}
+diff -r 894f003d9f5f src/managesieve/commands.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/managesieve/commands.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,45 @@
++#ifndef __COMMANDS_H
++#define __COMMANDS_H
++
++struct client_command_context;
++
++#include "managesieve-parser.h"
++
++typedef bool command_func_t(struct client_command_context *cmd);
++
++struct command {
++	const char *name;
++	command_func_t *func;
++};
++
++/* Register command. Given name parameter must be permanently stored until
++   command is unregistered. */
++void command_register(const char *name, command_func_t *func);
++void command_unregister(const char *name);
++
++/* Register array of commands. */
++void command_register_array(const struct command *cmdarr, unsigned int count);
++void command_unregister_array(const struct command *cmdarr, unsigned int count);
++
++command_func_t *command_find(const char *name);
++
++void commands_init(void);
++void commands_deinit(void);
++
++/* MANAGESIEVE commands: */
++
++/* Non-Authenticated State */
++bool cmd_logout(struct client_command_context *cmd);
++
++bool cmd_capability(struct client_command_context *cmd);
++
++/* Authenticated State */
++bool cmd_putscript(struct client_command_context *cmd);
++bool cmd_getscript(struct client_command_context *cmd);
++bool cmd_setactive(struct client_command_context *cmd);
++bool cmd_deletescript(struct client_command_context *cmd);
++bool cmd_listscripts(struct client_command_context *cmd);
++bool cmd_havespace(struct client_command_context *cmd);
++
++
++#endif
+diff -r 894f003d9f5f src/managesieve/common.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/managesieve/common.h	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,38 @@
++#ifndef __COMMON_H
++#define __COMMON_H
++
++#include "lib.h"
++#include "client.h"
++
++/* Disconnect client after idling this many seconds */
++#define CLIENT_IDLE_TIMEOUT (60*30)
++
++/* If we can't send anything to client for this long, disconnect the client */
++#define CLIENT_OUTPUT_TIMEOUT (5*60)
++
++/* Stop buffering more data into output stream after this many bytes */
++#define CLIENT_OUTPUT_OPTIMAL_SIZE 2048
++
++/* Disconnect client when it sends too many bad commands in a row */
++#define CLIENT_MAX_BAD_COMMANDS 20
++
++/* RFC-2683 recommends at least 8000 bytes. Some clients however don't
++   break large message sets to multiple commands, so we're pretty liberal
++   by default. */
++#define DEFAULT_MANAGESIEVE_MAX_LINE_LENGTH 65536
++
++#define DEFAULT_MANAGESIEVE_IMPLEMENTATION_STRING PACKAGE
++
++enum client_workarounds {
++  WORKAROUND_DELAY_NONE    = 0x00,
++};
++
++extern struct ioloop *ioloop;
++extern unsigned int managesieve_max_line_length;
++extern const char *managesieve_implementation_string;
++extern enum client_workarounds client_workarounds;
++
++//extern void (*hook_mail_storage_created)(struct sieve_storage *storage);
++extern void (*hook_client_created)(struct client **client);
++
++#endif
+diff -r 894f003d9f5f src/managesieve/main.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/src/managesieve/main.c	Sun May 04 16:00:59 2008 +0200
+@@ -0,0 +1,292 @@
++#include "common.h"
++#include "ioloop.h"
++#include "network.h"
++#include "ostream.h"
++#include "str.h"
++#include "lib-signals.h"
++#include "restrict-access.h"
++#include "fd-close-on-exec.h"
++#include "process-title.h"
++#include "randgen.h"
++#include "module-dir.h"
++#include "dict-client.h"
++#include "commands.h"
++#include "sieve-storage.h"
++#include "sieve-implementation.h"
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <syslog.h>
++
++#define IS_STANDALONE() \
++        (getenv("LOGGED_IN") == NULL)
++
++#define CRITICAL_MSG \
++  "Internal error occured. Refer to server log for more information."
++#define CRITICAL_MSG_STAMP CRITICAL_MSG " [%Y-%m-%d %H:%M:%S]"
++
++struct client_workaround_list {
++	const char *name;
++	enum client_workarounds num;
++};
++
++struct client_workaround_list client_workaround_list[] = {
++	{ NULL, 0 }
++};
++
++struct ioloop *ioloop;
++unsigned int managesieve_max_line_length;
++const char *managesieve_implementation_string;
++enum client_workarounds client_workarounds = 0;
++static struct io *log_io = NULL;
++
++static char log_prefix[128]; /* syslog() needs this to be permanent */
++
++//void (*hook_mail_storage_created)(struct mail_storage *storage) = NULL;
++void (*hook_client_created)(struct client **client) = NULL;
++
++static void sig_die(int signo, void *context __attr_unused__)
++{
++	/* warn about being killed because of some signal, except SIGINT (^C)
++	   which is too common at least while testing :) */
++	if (signo != SIGINT)
++		i_warning("Killed with signal %d", signo);
++	io_loop_stop(ioloop);
++}
++
++static void log_error_callback(void *context __attr_unused__)
++{
++	io_loop_stop(ioloop);
++}
++
++static void parse_workarounds(void)
++{
++	struct client_workaround_list *list;
++	const char *env, *const *str;
++
++	env = getenv("MANAGESIEVE_CLIENT_WORKAROUNDS");
++	if (env == NULL)
++		return;
++
++	for (str = t_strsplit_spaces(env, " ,"); *str != NULL; str++) {
++		list = client_workaround_list;
++		for (; list->name != NULL; list++) {
++			if (strcasecmp(*str, list->name) == 0) {
++				client_workarounds |= list->num;
++				break;
++			}
++		}
++		if (list->name == NULL)
++			i_fatal("Unknown client workaround: %s", *str);
++	}
++}
++
++static void open_logfile(void)
++{
++	const char *user;
++
++	if (getenv("LOG_TO_MASTER") != NULL) {
++		i_set_failure_internal();
++		return;
++	}
++
++ 	if (getenv("LOG_PREFIX") != NULL)
++		strocpy(log_prefix, getenv("LOG_PREFIX"), sizeof(log_prefix));
++	else {
++		user = getenv("USER");
++		if (user == NULL) {
++			if (IS_STANDALONE())
++				user = getlogin();
++			if (user == NULL)
++				user = "??";
++		}
++		if (strlen(user) >= sizeof(log_prefix)-6) {	
++			/* quite a long user name, cut it */
++ 			user = t_strndup(user, sizeof(log_prefix)-6-2);
++			user = t_strconcat(user, "..", NULL);
++		}
++		i_snprintf(log_prefix, sizeof(log_prefix), "imap(%s): ", user);
++	}
++
++	if (getenv("USE_SYSLOG") != NULL) {
++		const char *env = getenv("SYSLOG_FACILITY");
++		i_set_failure_syslog(log_prefix, LOG_NDELAY,
++				     env == NULL ? LOG_MAIL : atoi(env));
++	} else {
++		/* log to file or stderr */
++		i_set_failure_file(getenv("LOGFILE"), log_prefix);
++	}
++
++	if (getenv("INFOLOGFILE") != NULL)
++		i_set_info_file(getenv("INFOLOGFILE"));
++
++	i_set_failure_timestamp_format(getenv("LOGSTAMP"));
++}
++
++static void drop_privileges(void)
++{
++	const char *version;
++
++	version = getenv("DOVECOT_VERSION");
++	if (version != NULL && strcmp(version, PACKAGE_VERSION) != 0) {
++		i_fatal("Dovecot version mismatch: "
++			"Master is v%s, managesieve is v"PACKAGE_VERSION" "
++			"(if you don't care, set version_ignore=yes)", version);
++	}
++
++	/* Log file or syslog opening probably requires roots */
++	open_logfile();
++
++	/* Most likely needed. Have to open /dev/urandom before possible
++	   chrooting. */
++	random_init();
++
++    restrict_access_by_env(!IS_STANDALONE());
++}
++
++static void internal_error()
++{
++  struct tm *tm;
++  char str[256];
++
++  tm = localtime(&ioloop_time);
++
++  printf("BYE \"%s\"\n",
++    strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ?
++    i_strdup(str) : i_strdup(CRITICAL_MSG));
++}
++
++static void main_init(void)
++{
++	struct sieve_storage *storage;
++	struct client *client;
++	const char *user, *str, *sieve_storage, *mail;
++
++	lib_signals_init();
++        lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL);
++        lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL);
++        lib_signals_ignore(SIGPIPE);
++        lib_signals_set_handler(SIGALRM, FALSE, NULL, NULL);
++
++	user = getenv("USER");
++	if (user == NULL) {
++		if (IS_STANDALONE())
++			user = getlogin();
++		if (user == NULL) {
++			internal_error();
++			i_fatal("USER environment missing");
++		}
++	}
++
++	if (getenv("DEBUG") != NULL) {
++		const char *home;
++
++        home = getenv("HOME");
++        i_info("Effective uid=%s, gid=%s, home=%s",
++               dec2str(geteuid()), dec2str(getegid()),
++               home != NULL ? home : "(none)");
++	}
++	
++	if (getenv("STDERR_CLOSE_SHUTDOWN") != NULL) {
++		/* If master dies, the log fd gets closed and we'll quit */
++		log_io = io_add(STDERR_FILENO, IO_ERROR,
++				log_error_callback, NULL);
++	}
++
++	sieve_init();
++	sieve_set_implementation("cmu");
++
++	dict_client_register();
++	clients_init();
++	commands_init();
++
++	/* Settings */
++	str = getenv("MANAGESIEVE_MAX_LINE_LENGTH");
++	managesieve_max_line_length = str != NULL ?
++		(unsigned int)strtoul(str, NULL, 10) :
++		DEFAULT_MANAGESIEVE_MAX_LINE_LENGTH;
++
++	str = getenv("MANAGESIEVE_IMPLEMENTATION_STRING");
++	managesieve_implementation_string = str != NULL ?
++    str : DEFAULT_MANAGESIEVE_IMPLEMENTATION_STRING;
++
++	mail = getenv("MAIL"); 
++	sieve_storage = getenv("SIEVE_STORAGE");
++	if ( (sieve_storage == NULL || *sieve_storage == '\0') && 
++		!(mail == NULL || *mail == '\0') ) { 
++		storage = sieve_storage_create_from_mail(mail, user);
++	} else 
++		storage = sieve_storage_create(sieve_storage, user);
++
++	if (storage == NULL) { 
++    	internal_error();
++
++		/* failed */
++		if (sieve_storage != NULL && *sieve_storage != '\0')   
++			i_fatal("Failed to create sieve storage with data: %s", sieve_storage);
++		else if (mail != NULL && *mail != '\0')   
++			i_fatal("Failed to create sieve storage with mail-data: %s", mail);
++		else {
++			const char *home;
++	    
++			home = getenv("HOME");
++			if (home == NULL) home = "not set";
++	    
++			i_fatal("SIEVE_STORAGE and MAIL environment missing and "
++				"autodetection failed (home %s)", home);
++		}
++	}
++	
++	parse_workarounds();
++
++	client = client_create(0, 1, storage);
++	
++	client_send_ok(client, "Logged in.");
++}
++
++static void main_deinit(void)
++{
++	if (log_io != NULL)
++		io_remove(&log_io);
++	clients_deinit();
++
++	commands_deinit();
++	dict_client_unregister();
++	sieve_deinit();
++	random_deinit();
++
++	lib_signals_deinit();
++	closelog();
++}
++
++int main(int argc __attr_unused__, char *argv[], char *envp[])
++{
++#ifdef DEBUG
++	if (getenv("LOGGED_IN") != NULL && getenv("GDB") == NULL)
++		fd_debug_verify_leaks(3, 1024);
++#endif
++	if (IS_STANDALONE() && getuid() == 0 &&
++	    net_getpeername(1, NULL, NULL) == 0) {
++		printf("NO \"managesieve binary must not be started from "
++		       "inetd, use managesieve-login instead.\"\n");
++		return 1;
++	}
++
++	/* NOTE: we start rooted, so keep the code minimal until
++	   restrict_access_by_env() is called */
++	lib_init();
++	drop_privileges();
++
++	process_title_init(argv, envp);
++	ioloop = io_loop_create(system_pool);
++
++	main_init();
++	io_loop_run(ioloop);
++	main_deinit();
++
++	io_loop_destroy(&ioloop);
++	lib_deinit();
++
++	return 0;
++}
+diff -r 894f003d9f5f src/master/common.h
+--- a/src/master/common.h	Sun Mar 09 12:50:11 2008 +0200
++++ b/src/master/common.h	Sun May 04 16:00:59 2008 +0200
+@@ -17,6 +17,7 @@ enum process_type {
+ 	PROCESS_TYPE_SSL_PARAM,
+ 	PROCESS_TYPE_DICT,
+ 
++	PROCESS_TYPE_MANAGESIEVE,
+ 	PROCESS_TYPE_MAX
+ };
+ 
+diff -r 894f003d9f5f src/master/login-process.c
+--- a/src/master/login-process.c	Sun Mar 09 12:50:11 2008 +0200
++++ b/src/master/login-process.c	Sun May 04 16:00:59 2008 +0200
+@@ -67,8 +67,20 @@ static void login_group_create(struct se
+ 	group = i_new(struct login_group, 1);
+ 	group->refcount = 1;
+ 	group->set = set;
+-	group->process_type = set->protocol == MAIL_PROTOCOL_IMAP ?
+-		PROCESS_TYPE_IMAP : PROCESS_TYPE_POP3;
++
++	switch ( set->protocol ) {
++		case MAIL_PROTOCOL_IMAP:
++			group->process_type = PROCESS_TYPE_IMAP;
++			break;
++		case MAIL_PROTOCOL_POP3:
++			group->process_type = PROCESS_TYPE_POP3;
++			break;
++		case MAIL_PROTOCOL_MANAGESIEVE:
++			group->process_type = PROCESS_TYPE_MANAGESIEVE;
++			break;
++		default:
++			i_unreached();
++	}
+ 
+ 	group->next = login_groups;
+ 	login_groups = group;
+@@ -222,6 +234,8 @@ static void login_process_groups_create(
+ 			login_group_create(server->imap);
+ 		if (server->pop3 != NULL)
+ 			login_group_create(server->pop3);
++		if (server->managesieve != NULL)
++			login_group_create(server->managesieve);
+ 	}
+ }
+ 
+@@ -283,6 +297,8 @@ static bool login_process_read_group(str
+ 			protocol = MAIL_PROTOCOL_IMAP;
+ 		else if (strcmp(proto, "pop3") == 0)
+ 			protocol = MAIL_PROTOCOL_POP3;
++		else if (strcmp(proto, "managesieve") == 0)
++			protocol = MAIL_PROTOCOL_MANAGESIEVE;
+ 		else {
+ 			i_error("login: Unknown protocol '%s'", proto);
+ 			return FALSE;
+@@ -580,6 +596,9 @@ static void login_process_init_env(struc
+ 				    *set->imap_capability != '\0' ?
+ 				    set->imap_capability :
+ 				    set->imap_generated_capability, NULL));
++	} else if (group->process_type == PROCESS_TYPE_MANAGESIEVE) {
++		env_put(t_strconcat("MANAGESIEVE_IMPLEMENTATION_STRING=",
++			set->managesieve_implementation_string, NULL));
+ 	}
+ }
+ 
+diff -r 894f003d9f5f src/master/mail-process.c
+--- a/src/master/mail-process.c	Sun Mar 09 12:50:11 2008 +0200
++++ b/src/master/mail-process.c	Sun May 04 16:00:59 2008 +0200
+@@ -231,6 +231,25 @@ mail_process_set_environment(struct sett
+ 			    set->pop3_client_workarounds, NULL));
+ 	env_put(t_strconcat("POP3_LOGOUT_FORMAT=",
+ 			    set->pop3_logout_format, NULL));
++
++	env_put(t_strdup_printf("MANAGESIEVE_MAX_LINE_LENGTH=%u",
++				set->managesieve_max_line_length));
++	env_put(t_strconcat("MANAGESIEVE_IMPLEMENTATION_STRING=",
++        set->managesieve_implementation_string, NULL));
++
++	/* Set sieve environment 
++	 *   FIXME: Currently just uses the expand_mail_env function to 
++	 *   substitute variables and home dir. Technically, that function
++	 *   is not meant for the sieve implementation.  
++	 */
++	if ( set->sieve_storage != NULL ) {
++		env_put(t_strconcat("SIEVE_STORAGE=",
++        	expand_mail_env(set->sieve_storage, var_expand_table), NULL));
++	}
++	if (set->sieve != NULL) {
++		env_put(t_strconcat("SIEVE=",
++			expand_mail_env(set->sieve, var_expand_table), NULL));
++	}
+ 
+ 	if (set->mail_save_crlf)
+ 		env_put("MAIL_SAVE_CRLF=1");
+@@ -348,6 +367,8 @@ void mail_process_exec(const char *proto
+ 			set = server->imap;
+ 		else if (strcmp(protocol, "pop3") == 0)
+ 			set = server->pop3;
++		else if (strcmp(protocol, "managesieve") == 0)
++			set = server->managesieve;
+ 		else
+ 			i_fatal("Unknown protocol: '%s'", protocol);
+ 		executable = set->mail_executable;
+diff -r 894f003d9f5f src/master/main.c
+--- a/src/master/main.c	Sun Mar 09 12:50:11 2008 +0200
++++ b/src/master/main.c	Sun May 04 16:00:59 2008 +0200
+@@ -33,7 +33,8 @@ const char *process_names[PROCESS_TYPE_M
+ 	"imap",
+ 	"pop3",
+ 	"ssl-build-param",
+-	"dict"
++	"dict",
++	 "managesieve"
+ };
+ 
+ static const char *configfile = SYSCONFDIR "/" PACKAGE ".conf";
+@@ -241,6 +242,7 @@ static void sigchld_handler(int signo __
+ 			break;
+ 		case PROCESS_TYPE_IMAP:
+ 		case PROCESS_TYPE_POP3:
++		case PROCESS_TYPE_MANAGESIEVE:
+ 			mail_process_destroyed(pid);
+ 			break;
+ 		case PROCESS_TYPE_SSL_PARAM:
+@@ -354,6 +356,10 @@ static void check_conflicts(const struct
+ 			check_conflicts_set(server->pop3, ip, port,
+ 					    "pop3", proto);
+ 		}
++		if (server->managesieve != NULL) {
++			check_conflicts_set(server->managesieve, ip, port,
++					    "managesieve", proto);
++		}
+ 	}
+ }
+ 
+@@ -364,13 +370,31 @@ static void listen_protocols(struct sett
+ 	unsigned int port;
+ 	int *fd, i;
+ 
+-	set->listen_port = set->protocol == MAIL_PROTOCOL_IMAP ? 143 : 110;
++	switch (set->protocol) {
++	case MAIL_PROTOCOL_IMAP:
++		set->listen_port = 143;
+ #ifdef HAVE_SSL
+-	set->ssl_listen_port = set->protocol == MAIL_PROTOCOL_IMAP ? 993 : 995;
++		set->ssl_listen_port = 993;
+ #else
+-	set->ssl_listen_port = 0;
+-#endif
+-
++		set->ssl_listen_port = 0;
++#endif
++		break;
++	case MAIL_PROTOCOL_POP3:
++		set->listen_port = 110;
++#ifdef HAVE_SSL
++		set->ssl_listen_port = 995;
++#else
++		set->ssl_listen_port = 0;
++#endif
++		break;
++	case MAIL_PROTOCOL_MANAGESIEVE:
++		set->listen_port = 2000;
++		set->ssl_listen_port = 0;
++		break;
++	default:
++		i_unreached();
++	}
++ 
+ 	/* resolve */
+ 	resolve_ip("listen", set->listen, &set->listen_ip, &set->listen_port);
+ 	if (!set->ssl_disable) {
+@@ -413,6 +437,12 @@ static void listen_protocols(struct sett
+ 				port = set->ssl_listen_port;
+ 				ip = &set->ssl_listen_ip;
+ 			}
++		} else if (strcasecmp(*proto, "managesieve") == 0) {
++			if (set->protocol == MAIL_PROTOCOL_MANAGESIEVE) {
++				fd = &set->listen_fd;
++				port = set->listen_port;
++				ip = &set->listen_ip;
++			}
+ 		} else {
+ 			i_fatal("Unknown protocol %s", *proto);
+ 		}
+@@ -466,12 +496,14 @@ static void listen_fds_open(bool retry)
+ static void listen_fds_open(bool retry)
+ {
+ 	struct server_settings *server;
+-
++  
+ 	for (server = settings_root; server != NULL; server = server->next) {
+ 		if (server->imap != NULL)
+ 			listen_protocols(server->imap, retry);
+ 		if (server->pop3 != NULL)
+ 			listen_protocols(server->pop3, retry);
++		if (server->managesieve != NULL)
++			listen_protocols(server->managesieve, retry);
+ 	}
+ }
+ 
+@@ -494,6 +526,11 @@ static void listen_fds_close(struct serv
+ 			    close(server->pop3->ssl_listen_fd) < 0)
+ 				i_error("close(pop3.ssl_listen_fd) failed: %m");
+ 		}
++		if (server->managesieve != NULL) {
++			if (server->managesieve->listen_fd != null_fd &&
++			    close(server->managesieve->listen_fd) < 0)
++				i_error("close(managesieve.listen_fd) failed: %m");
++		}
+ 	}
+ }
+ 
+@@ -516,6 +553,8 @@ static bool have_stderr(struct server_se
+ 		if (server->imap != NULL && have_stderr_set(server->imap))
+ 			return TRUE;
+ 		if (server->pop3 != NULL && have_stderr_set(server->pop3))
++			return TRUE;
++		if (server->managesieve != NULL && have_stderr_set(server->managesieve))
+ 			return TRUE;
+ 
+ 		server = server->next;
+diff -r 894f003d9f5f src/master/master-settings-defs.c
+--- a/src/master/master-settings-defs.c	Sun Mar 09 12:50:11 2008 +0200
++++ b/src/master/master-settings-defs.c	Sun May 04 16:00:59 2008 +0200
+@@ -115,5 +115,13 @@ static struct setting_def setting_defs[]
+ 	DEF(SET_STR, pop3_client_workarounds),
+ 	DEF(SET_STR, pop3_logout_format),
+ 
++  	/* managesieve */
++  	DEF(SET_INT, managesieve_max_line_length),
++  	DEF(SET_STR, managesieve_implementation_string),
++
++	/* sieve */
++  	DEF(SET_STR, sieve_storage),
++  	DEF(SET_STR, sieve),
++
+ 	{ 0, NULL, 0 }
+ };
+diff -r 894f003d9f5f src/master/master-settings.c
+--- a/src/master/master-settings.c	Sun Mar 09 12:50:11 2008 +0200
++++ b/src/master/master-settings.c	Sun May 04 16:00:59 2008 +0200
+@@ -269,6 +269,14 @@ struct settings default_settings = {
+ 	MEMBER(pop3_client_workarounds) "",
+ 	MEMBER(pop3_logout_format) "top=%t/%p, retr=%r/%b, del=%d/%m, size=%s",
+ 
++	/* managesieve */
++	MEMBER(managesieve_max_line_length) 65536,
++	MEMBER(managesieve_implementation_string) PACKAGE,
++
++	/* sieve */
++	MEMBER(sieve_storage) "",
++	MEMBER(sieve) NULL,
++
+ 	/* .. */
+ 	MEMBER(listen_fd) -1,
+ 	MEMBER(ssl_listen_fd) -1
+@@ -425,6 +433,8 @@ static bool auth_settings_verify(struct 
+ 			auth->parent->pop3->ssl_verify_client_cert = TRUE;
+ 		if (auth->parent->imap != NULL)
+ 			auth->parent->imap->ssl_verify_client_cert = TRUE;
++		if (auth->parent->managesieve != NULL)
++			auth->parent->managesieve->ssl_verify_client_cert = TRUE;
+ 	}
+ 
+ 	for (s = auth->sockets; s != NULL; s = s->next) {
+@@ -475,8 +485,11 @@ static bool settings_is_active(struct se
+ 	if (set->protocol == MAIL_PROTOCOL_IMAP) {
+ 		if (strstr(set->protocols, "imap") == NULL)
+ 			return FALSE;
++	} else if (set->protocol == MAIL_PROTOCOL_POP3) {
++		if (strstr(set->protocols, "pop3") == NULL)
++			return FALSE;
+ 	} else {
+-		if (strstr(set->protocols, "pop3") == NULL)
++		if (strstr(set->protocols, "managesieve") == NULL)
+ 			return FALSE;
+ 	}
+ 
+@@ -1081,7 +1094,7 @@ static const char *parse_setting(const c
+ 
+ 	if (strcmp(key, "login") == 0) {
+ 		i_warning("Ignoring deprecated 'login' section handling. "
+-			  "Use protocol imap/pop3 { .. } instead. "
++			  "Use protocol imap/pop3/managesieve { .. } instead. "
+ 			  "Some settings may have been read incorrectly.");
+ 		return NULL;
+ 	}
+@@ -1104,6 +1117,15 @@ static const char *parse_setting(const c
+ 			error = parse_setting_from_defs(settings_pool,
+ 							setting_defs,
+ 							ctx->server->pop3,
++							key, value);
++		}
++
++		if (error == NULL &&
++		    (ctx->protocol == MAIL_PROTOCOL_ANY ||
++		     ctx->protocol == MAIL_PROTOCOL_MANAGESIEVE)) {
++			error = parse_setting_from_defs(settings_pool,
++							setting_defs,
++							ctx->server->managesieve,
+ 							key, value);
+ 		}
+ 
+@@ -1165,6 +1187,13 @@ static const char *parse_setting(const c
+ 			array_append(&ctx->server->pop3->plugin_envs,
+ 				     &value, 1);
+ 		}
++		if (ctx->protocol == MAIL_PROTOCOL_ANY ||
++		    ctx->protocol == MAIL_PROTOCOL_MANAGESIEVE) {
++			array_append(&ctx->server->managesieve->plugin_envs, &key, 1);
++			array_append(&ctx->server->managesieve->plugin_envs,
++				     &value, 1);
++		}
++
+ 		return NULL;
+ 	}
+ 
+@@ -1174,7 +1203,8 @@ static struct server_settings *
+ static struct server_settings *
+ create_new_server(const char *name,
+ 		  struct settings *imap_defaults,
+-		  struct settings *pop3_defaults)
++		  struct settings *pop3_defaults,
++		  struct settings *managesieve_defaults)
+ {
+ 	struct server_settings *server;
+ 
+@@ -1182,16 +1212,20 @@ create_new_server(const char *name,
+ 	server->name = p_strdup(settings_pool, name);
+ 	server->imap = p_new(settings_pool, struct settings, 1);
+ 	server->pop3 = p_new(settings_pool, struct settings, 1);
++	server->managesieve = p_new(settings_pool, struct settings, 1);
+ 	server->auth_defaults = default_auth_settings;
+ 
+ 	*server->imap = *imap_defaults;
+ 	*server->pop3 = *pop3_defaults;
++	*server->managesieve = *managesieve_defaults;
+ 
+ 	ARRAY_CREATE(&server->dicts, settings_pool, const char *, 4);
+ 	ARRAY_CREATE(&server->imap->plugin_envs, settings_pool,
+ 		     const char *, 8);
+ 	ARRAY_CREATE(&server->pop3->plugin_envs, settings_pool,
+ 		     const char *, 8);
++	ARRAY_CREATE(&server->managesieve->plugin_envs, settings_pool,
++		     const char *, 8);
+ 
+ 	server->imap->server = server;
+ 	server->imap->protocol = MAIL_PROTOCOL_IMAP;
+@@ -1205,6 +1239,12 @@ create_new_server(const char *name,
+ 	server->pop3->mail_executable = PKG_LIBEXECDIR"/pop3";
+ 	server->pop3->mail_plugin_dir = MODULEDIR"/pop3";
+ 
++	server->managesieve->server = server;
++	server->managesieve->protocol = MAIL_PROTOCOL_MANAGESIEVE;
++	server->managesieve->login_executable = PKG_LIBEXECDIR"/managesieve-login";
++	server->managesieve->mail_executable = PKG_LIBEXECDIR"/managesieve";
++	server->managesieve->mail_plugin_dir = MODULEDIR"/managesieve";
++  
+ 	return server;
+ }
+ 
+@@ -1248,8 +1288,8 @@ static bool parse_section(const char *ty
+ 
+ 		ctx->type = SETTINGS_TYPE_SERVER;
+ 		ctx->server = create_new_server(name, ctx->server->imap,
+-						ctx->server->pop3);
+-                server = ctx->root;
++						ctx->server->pop3, ctx->server->managesieve);
++		server = ctx->root;
+ 		while (server->next != NULL)
+ 			server = server->next;
+ 		server->next = ctx->server;
+@@ -1270,6 +1310,8 @@ static bool parse_section(const char *ty
+ 			ctx->protocol = MAIL_PROTOCOL_POP3;
+ 		else if (strcmp(name, "lda") == 0)
+ 			ctx->protocol = MAIL_PROTOCOL_LDA;
++		else if (strcmp(name, "managesieve") == 0)
++			ctx->protocol = MAIL_PROTOCOL_MANAGESIEVE;
+ 		else {
+ 			*errormsg = "Unknown protocol name";
+ 			return FALSE;
+@@ -1380,7 +1422,7 @@ bool master_settings_read(const char *pa
+ 	ctx.protocol = MAIL_PROTOCOL_ANY;
+ 	ctx.server = ctx.root =
+ 		create_new_server("default",
+-				  &default_settings, &default_settings);
++				  &default_settings, &default_settings, &default_settings);
+ 	ctx.auth = &ctx.server->auth_defaults;
+ 
+ 	if (!settings_read(path, NULL, parse_setting, parse_section, &ctx))
+@@ -1397,7 +1439,9 @@ bool master_settings_read(const char *pa
+ 
+ 	if (!nochecks && !nofixes) {
+ 		ctx.root->defaults = settings_is_active(ctx.root->imap) ?
+-			ctx.root->imap : ctx.root->pop3;
++			ctx.root->imap : 
++			(settings_is_active(ctx.root->pop3) ? 
++				ctx.root->pop3 : ctx.root->managesieve);
+ 
+ 		path = t_strconcat(ctx.root->defaults->base_dir,
+ 				   "/master.pid", NULL);
+@@ -1407,7 +1451,8 @@ bool master_settings_read(const char *pa
+ 	prev = NULL;
+ 	for (server = ctx.root; server != NULL; server = server->next) {
+ 		if ((*server->imap->protocols == '\0' ||
+-		     *server->pop3->protocols == '\0') && !nochecks) {
++		     *server->pop3->protocols == '\0' ||
++		     *server->managesieve->protocols == '\0') && !nochecks) {
+ 			i_error("No protocols given in configuration file");
+ 			return FALSE;
+ 		}
+@@ -1437,6 +1482,15 @@ bool master_settings_read(const char *pa
+ 				server->defaults = server->pop3;
+ 		}
+ 
++		if (!settings_is_active(server->managesieve) && !nochecks)
++			server->managesieve = NULL;
++		else {
++			if (!settings_fix(server->managesieve, nochecks, nofixes))
++				return FALSE;
++			if (server->defaults == NULL)
++				server->defaults = server->managesieve;
++		}
++
+ 		if (server->defaults == NULL) {
+ 			if (prev == NULL)
+ 				ctx.root = server->next;
+@@ -1653,8 +1707,8 @@ static void dict_settings_dump(const str
+ 
+ void master_settings_dump(struct server_settings *set, bool nondefaults)
+ {
+-	const void *sets[4];
+-	const char *set_names[4];
++	const void *sets[5];
++	const char *set_names[5];
+ 	unsigned int count;
+ 
+ 	sets[0] = &default_settings;
+@@ -1673,6 +1727,11 @@ void master_settings_dump(struct server_
+ 		sets[count] = set->pop3;
+ 		set_names[count] = "pop3";
+ 		count++;
++	}	
++	if (set->managesieve != NULL) {
++		sets[count] = set->managesieve;
++		set_names[count] = "managesieve";
++		count++;
+ 	}
+ 	settings_dump(setting_defs, sets, set_names, count, nondefaults, 0);
+ 	namespace_settings_dump(set->namespaces, nondefaults);
+diff -r 894f003d9f5f src/master/master-settings.h
+--- a/src/master/master-settings.h	Sun Mar 09 12:50:11 2008 +0200
++++ b/src/master/master-settings.h	Sun May 04 16:00:59 2008 +0200
+@@ -4,10 +4,11 @@
+ #include "network.h"
+ 
+ enum mail_protocol {
+-        MAIL_PROTOCOL_ANY,
+-        MAIL_PROTOCOL_IMAP,
++	MAIL_PROTOCOL_ANY,
++	MAIL_PROTOCOL_IMAP,
+ 	MAIL_PROTOCOL_POP3,
+-	MAIL_PROTOCOL_LDA
++	MAIL_PROTOCOL_LDA,
++	MAIL_PROTOCOL_MANAGESIEVE
+ };
+ 
+ struct settings {
+@@ -123,6 +124,14 @@ struct settings {
+ 	const char *pop3_client_workarounds;
+ 	const char *pop3_logout_format;
+ 
++	/* managesieve */
++	unsigned int managesieve_max_line_length;
++	const char *managesieve_implementation_string;
++
++	/* sieve */
++	const char *sieve_storage;
++	const char *sieve;
++
+ 	/* .. */
+ 	int listen_fd, ssl_listen_fd;
+ 
+@@ -231,9 +240,10 @@ struct server_settings {
+ 	struct settings *defaults;
+ 	struct settings *imap;
+ 	struct settings *pop3;
++	struct settings *managesieve;
+ 	struct auth_settings *auths;
+ 	struct auth_settings auth_defaults;
+-        struct namespace_settings *namespaces;
++	struct namespace_settings *namespaces;
+ 
+ 	array_t ARRAY_DEFINE(dicts, const char *);
+ 
--- dovecot-1.0.15.orig/debian/patches/pam-error-information.dpatch
+++ dovecot-1.0.15/debian/patches/pam-error-information.dpatch
@@ -0,0 +1,17 @@
+#! /bin/sh -e
+
+. $(dirname $0)/DPATCH
+
+exit 0
+@DPATCH@
+diff -urN dovecot-1.0.7/src/auth/passdb-pam.c dovecot-1.0.7.debian/src/auth/passdb-pam.c
+--- dovecot-1.0.7/src/auth/passdb-pam.c	2007-10-28 02:09:23.000000000 +0200
++++ dovecot-1.0.7.debian/src/auth/passdb-pam.c	2007-11-14 21:22:36.000000000 +0100
+@@ -273,6 +273,7 @@
+ 		   care if they do. */
+ 		if (host != NULL)
+ 			(void)pam_set_item(pamh, PAM_RHOST, host);
++        (void)pam_set_item(pamh, PAM_RUSER, request->user);
+ 		/* TTY is needed by eg. pam_access module */
+ 		(void)pam_set_item(pamh, PAM_TTY, "dovecot");
+ 
--- dovecot-1.0.15.orig/debian/patches/CVE-2008-4577.dpatch
+++ dovecot-1.0.15/debian/patches/CVE-2008-4577.dpatch
@@ -0,0 +1,21 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## CVE-2008-4577.dpatch by Timo Sirainen
+##
+## DP: ACLs: Negative rights were actually treated as positive rights. 
+
+@DPATCH@
+
+--- a/src/plugins/acl/acl-cache.c	Wed Apr 30 20:22:57 2008 +0300
++++ b/src/plugins/acl/acl-cache.c	Mon Sep 08 16:56:13 2008 +0300
+@@ -375,10 +375,8 @@ acl_cache_my_current_rights_recalculate(
+ 			   rights. */
+ 			right_size = obj_cache->my_neg_rights[i]->size;
+ 			p = buffer_get_space_unsafe(bitmask, 0, right_size);
+-			for (j = 0; j < right_size; j++) {
+-				p[j] |=
+-					obj_cache->my_neg_rights[i]->mask[j];
+-			}
++			for (j = 0; j < right_size; j++)
++				p[j] &= ~obj_cache->my_neg_rights[i]->mask[j];
+ 		}
+ 	}
--- dovecot-1.0.15.orig/debian/patches/DPATCH
+++ dovecot-1.0.15/debian/patches/DPATCH
@@ -0,0 +1,16 @@
+[ -f debian/patches/00patch-opts ] && . debian/patches/00patch-opts
+patch_opts="${patch_opts:--f --no-backup-if-mismatch}"
+
+if [ $# -ne 1 ]; then
+    echo >&2 "`basename $0`: script expects -patch|-unpatch as argument"
+    exit 1
+fi
+case "$1" in
+       -patch) patch $patch_opts -p1 < $0;;
+       -unpatch) patch $patch_opts -p1 -R < $0;;
+        *)      
+                echo >&2 "`basename $0`: script expects -patch|-unpatch as argument"
+                exit 1;;
+esac
+
+exit 0
--- dovecot-1.0.15.orig/debian/patches/mbox_snarf.dpatch
+++ dovecot-1.0.15/debian/patches/mbox_snarf.dpatch
@@ -0,0 +1,304 @@
+#! /bin/sh -e
+
+## DP: Hack to support mbox snarfing, Based heavily on email found here:
+## DP: http://dovecot.org/list/dovecot/2007-May/022803.html
+## DP: Author: Timo Sirainen
+
+. $(dirname $0)/DPATCH
+
+exit 0
+@DPATCH@
+diff -uwNr ./dovecot_patched/dovecot-1.0.10/configure.in ./dovecot_compile/dovecot-1.0.10/configure.in
+--- dovecot-1.0.10/configure.in	2007-12-29 07:06:24.000000000 +0100
++++ dovecot-1.0.10/configure.in	2008-01-23 22:25:54.000000000 +0100
+@@ -1854,6 +1854,7 @@
+ src/plugins/imap-quota/Makefile
+ src/plugins/lazy-expunge/Makefile
+ src/plugins/mail-log/Makefile
++src/plugins/mbox-snarf/Makefile
+ src/plugins/trash/Makefile
+ src/plugins/zlib/Makefile
+ stamp.h
+diff -uwNr ./dovecot_patched/dovecot-1.0.10/src/plugins/Makefile.am ./dovecot_compile/dovecot-1.0.10/src/plugins/Makefile.am
+--- dovecot-1.0.10/src/plugins/Makefile.am	2007-12-11 19:52:09.000000000 +0100
++++ dovecot-1.0.10/src/plugins/Makefile.am	2008-01-23 22:23:48.000000000 +0100
+@@ -2,4 +2,4 @@
+ ZLIB = zlib
+ endif
+ 
+-SUBDIRS = acl convert quota imap-quota lazy-expunge mail-log trash $(ZLIB)
++SUBDIRS = acl convert quota imap-quota lazy-expunge mail-log mbox-snarf trash $(ZLIB)
+diff -uwNr ./dovecot_patched/dovecot-1.0.10/src/plugins/mbox-snarf/Makefile.am ./dovecot_compile/dovecot-1.0.10/src/plugins/mbox-snarf/Makefile.am
+--- dovecot-1.0.10/src/plugins/mbox-snarf/Makefile.am	1970-01-01 01:00:00.000000000 +0100
++++ dovecot-1.0.10/src/plugins/mbox-snarf/Makefile.am	2008-01-23 22:48:34.000000000 +0100
+@@ -0,0 +1,24 @@
++AM_CPPFLAGS = \
++	-I$(top_srcdir)/src/lib \
++	-I$(top_srcdir)/src/lib-mail \
++	-I$(top_srcdir)/src/lib-storage \
++	-I$(top_srcdir)/src/lib-imap
++
++lib20_mbox_snarf_plugin_la_LDFLAGS = -module -avoid-version
++
++module_LTLIBRARIES = \
++	lib20_mbox_snarf_plugin.la
++
++lib20_mbox_snarf_plugin_la_SOURCES = \
++	mbox-snarf-plugin.c
++
++noinst_HEADERS = \
++	mbox-snarf-plugin.h
++
++install-exec-local:
++	for d in imap pop3 lda; do \
++	  $(mkdir_p) $(DESTDIR)$(moduledir)/$$d; \
++	  rm -f $(DESTDIR)$(moduledir)/$$d/lib20_mbox_snarf_plugin.so; \
++	  $(LN_S) ../lib20_mbox_snarf_plugin.so $(DESTDIR)$(moduledir)/$$d; \
++	done
++
+diff -uwNr ./dovecot_patched/dovecot-1.0.10/src/plugins/mbox-snarf/mbox-snarf-plugin.c ./dovecot_compile/dovecot-1.0.10/src/plugins/mbox-snarf/mbox-snarf-plugin.c
+--- dovecot-1.0.10/src/plugins/mbox-snarf/mbox-snarf-plugin.c	1970-01-01 01:00:00.000000000 +0100
++++ dovecot-1.0.10/src/plugins/mbox-snarf/mbox-snarf-plugin.c	2007-06-05 20:15:47.000000000 +0200
+@@ -0,0 +1,231 @@
++/* Copyright (C) 2007 Timo Sirainen */
++
++/*
++   export DOVECOT=~/src/dovecot-1.0.0
++   gcc -fPIC -shared -Wall -I$DOVECOT -I$DOVECOT/src/lib \
++     -I$DOVECOT/src/lib-storage -I$DOVECOT/src/lib-mail \
++     -I$DOVECOT/src/lib-imap -DHAVE_CONFIG_H \
++     mbox-snarf-plugin.c -o mbox_snarf_plugin.so
++*/
++
++#include "lib.h"
++#include "array.h"
++#include "home-expand.h"
++#include "mail-search.h"
++#include "mail-storage-private.h"
++
++#include <stdlib.h>
++#include <sys/stat.h>
++
++#define MBOX_SNARF_CONTEXT(obj) \
++	*((void **)array_idx_modifyable(&(obj)->module_contexts, \
++					mbox_snarf_storage_module_id))
++
++struct mbox_snarf_mail_storage {
++	struct mail_storage_vfuncs super;
++	bool internal_namespace;
++
++	const char *snarf_inbox_path;
++	bool open_spool_inbox;
++};
++
++struct mbox_snarf_mailbox {
++	struct mailbox_vfuncs super;
++
++	struct mailbox *spool_mbox;
++};
++
++/* defined by imap, pop3, lda */
++extern void (*hook_mail_storage_created)(struct mail_storage *storage);
++
++const char *mbox_snarf_plugin_version = PACKAGE_VERSION;
++
++static void (*mbox_snarf_next_hook_mail_storage_created)
++	(struct mail_storage *storage);
++
++static unsigned int mbox_snarf_storage_module_id = 0;
++static bool mbox_snarf_storage_module_id_set = FALSE;
++
++static int sync_mailbox(struct mailbox *box)
++{
++	struct mailbox_sync_context *ctx;
++        struct mailbox_sync_rec sync_rec;
++	struct mailbox_status status;
++
++	ctx = mailbox_sync_init(box, MAILBOX_SYNC_FLAG_FULL_READ);
++	while (mailbox_sync_next(ctx, &sync_rec) > 0)
++		;
++	return mailbox_sync_deinit(&ctx, &status);
++}
++
++static int mbox_snarf(struct mailbox *srcbox, struct mailbox *destbox)
++{
++	struct mail_search_arg search_arg;
++	struct mail_search_context *search_ctx;
++        struct mailbox_transaction_context *src_trans, *dest_trans;
++	struct mail *mail;
++	int ret;
++
++	if (sync_mailbox(srcbox) < 0)
++		return -1;
++
++	memset(&search_arg, 0, sizeof(search_arg));
++	search_arg.type = SEARCH_ALL;
++
++	src_trans = mailbox_transaction_begin(srcbox, 0);
++	dest_trans = mailbox_transaction_begin(destbox,
++					MAILBOX_TRANSACTION_FLAG_EXTERNAL);
++	search_ctx = mailbox_search_init(src_trans, NULL, &search_arg, NULL);
++
++	mail = mail_alloc(src_trans, MAIL_FETCH_STREAM_HEADER |
++			  MAIL_FETCH_STREAM_BODY, NULL);
++	while ((ret = mailbox_search_next(search_ctx, mail)) > 0) {
++		if (mail->expunged)
++			continue;
++
++		if (mailbox_copy(dest_trans, mail, 0, NULL, NULL) < 0) {
++			if (!mail->expunged)
++				break;
++		}
++		mail_expunge(mail);
++	}
++	mail_free(&mail);
++
++	if (mailbox_search_deinit(&search_ctx) < 0)
++		ret = -1;
++
++	/* commit the copied messages to the destination mailbox. if we crash
++	   between that and between expunging the messages from the source
++	   mailbox, we're left with duplicates. */
++	if (ret < 0)
++		mailbox_transaction_rollback(&dest_trans);
++	else if (mailbox_transaction_commit(&dest_trans, 0) < 0)
++		ret = -1;
++
++	if (ret < 0)
++		mailbox_transaction_rollback(&src_trans);
++	else {
++		if (mailbox_transaction_commit(&src_trans, 0) < 0)
++			ret = -1;
++	}
++	return ret;
++}
++
++static struct mailbox_sync_context *
++mbox_snarf_sync_init(struct mailbox *box, enum mailbox_sync_flags flags)
++{
++	struct mbox_snarf_mail_storage *mstorage =
++		MBOX_SNARF_CONTEXT(box->storage);
++	struct mbox_snarf_mailbox *mbox = MBOX_SNARF_CONTEXT(box);
++
++	if (mbox->spool_mbox == NULL) {
++		/* try to open the spool mbox */
++		mstorage->open_spool_inbox = TRUE;
++		mbox->spool_mbox =
++			mailbox_open(box->storage, "INBOX", NULL,
++				     MAILBOX_OPEN_KEEP_RECENT |
++				     MAILBOX_OPEN_NO_INDEX_FILES);
++		mstorage->open_spool_inbox = FALSE;
++	}
++
++	if (mbox->spool_mbox != NULL)
++		mbox_snarf(mbox->spool_mbox, box);
++
++	return mbox->super.sync_init(box, flags);
++}
++
++static int mbox_snarf_close(struct mailbox *box)
++{
++	struct mbox_snarf_mailbox *mbox = MBOX_SNARF_CONTEXT(box);
++
++	if (mbox->spool_mbox != NULL)
++		mailbox_close(&mbox->spool_mbox);
++
++	return mbox->super.close(box);
++}
++
++static struct mailbox *
++mbox_snarf_mailbox_open(struct mail_storage *storage, const char *name,
++			struct istream *input, enum mailbox_open_flags flags)
++{
++	struct mbox_snarf_mail_storage *mstorage =
++		MBOX_SNARF_CONTEXT(storage);
++	struct mailbox *box;
++	struct mbox_snarf_mailbox *mbox;
++	struct stat st;
++	enum mail_storage_flags old_flags = storage->flags;
++	bool use_snarfing = FALSE;
++
++	if (strcasecmp(name, "INBOX") == 0 && !mstorage->open_spool_inbox) {
++		if (stat(mstorage->snarf_inbox_path, &st) == 0) {
++			/* use ~/mbox as the INBOX */
++			name = mstorage->snarf_inbox_path;
++			use_snarfing = TRUE;
++			storage->flags |= MAIL_STORAGE_FLAG_FULL_FS_ACCESS;
++		} else if (errno != ENOENT) {
++			mail_storage_set_critical(storage,
++						  "stat(%s) failed: %m",
++						  mstorage->snarf_inbox_path);
++		}
++	}
++
++	box = mstorage->super.mailbox_open(storage, name, input, flags);
++	storage->flags = old_flags;
++
++	if (box == NULL || !use_snarfing)
++		return box;
++
++	mbox = p_new(box->pool, struct mbox_snarf_mailbox, 1);
++	mbox->super = box->v;
++
++	box->v.sync_init = mbox_snarf_sync_init;
++	box->v.close = mbox_snarf_close;
++	array_idx_set(&box->module_contexts,
++		      mbox_snarf_storage_module_id, &mbox);
++	return box;
++}
++
++static void mbox_snarf_mail_storage_created(struct mail_storage *storage)
++{
++	struct mbox_snarf_mail_storage *mstorage;
++
++	if (mbox_snarf_next_hook_mail_storage_created != NULL)
++		mbox_snarf_next_hook_mail_storage_created(storage);
++
++	mstorage = p_new(storage->pool, struct mbox_snarf_mail_storage, 1);
++	mstorage->snarf_inbox_path =
++		p_strdup(storage->pool, home_expand(getenv("MBOX_SNARF")));
++	mstorage->super = storage->v;
++	storage->v.mailbox_open = mbox_snarf_mailbox_open;
++
++	if (!mbox_snarf_storage_module_id_set) {
++		mbox_snarf_storage_module_id = mail_storage_module_id++;
++		mbox_snarf_storage_module_id_set = TRUE;
++	}
++
++	array_idx_set(&storage->module_contexts,
++		      mbox_snarf_storage_module_id, &mstorage);
++}
++
++void mbox_snarf_plugin_init(void);
++void mbox_snarf_plugin_deinit(void);
++
++void mbox_snarf_plugin_init(void)
++{
++	const char *path;
++
++	path = getenv("MBOX_SNARF");
++	if (path != NULL) {
++		mbox_snarf_next_hook_mail_storage_created =
++			hook_mail_storage_created;
++		hook_mail_storage_created = mbox_snarf_mail_storage_created;
++	}
++}
++
++void mbox_snarf_plugin_deinit(void)
++{
++	if (getenv("MBOX_SNARF") != NULL) {
++		hook_mail_storage_created =
++			mbox_snarf_next_hook_mail_storage_created;
++	}
++}
+diff -uwNr ./dovecot_patched/dovecot-1.0.10/src/plugins/mbox-snarf/mbox-snarf-plugin.h ./dovecot_compile/dovecot-1.0.10/src/plugins/mbox-snarf/mbox-snarf-plugin.h
+--- dovecot-1.0.10/src/plugins/mbox-snarf/mbox-snarf-plugin.h	1970-01-01 01:00:00.000000000 +0100
++++ dovecot-1.0.10/src/plugins/mbox-snarf/mbox-snarf-plugin.h	2008-01-23 22:41:03.000000000 +0100
+@@ -0,0 +1,7 @@
++#ifndef __MBOX_SNARF_PLUGIN_H
++#define __MBOX_SNARF_PLUGIN_H
++
++void mbox_snarf_plugin_init(void);
++void mbox_snarf_plugin_deinit(void);
++
++#endif
--- dovecot-1.0.15.orig/debian/patches/dovecot-example.dpatch
+++ dovecot-1.0.15/debian/patches/dovecot-example.dpatch
@@ -0,0 +1,241 @@
+#! /bin/sh -e
+
+## DP: Small changes to dovecot-example.conf
+## DP: Author: Jaldhar H. Vyas <jaldhar@debian.org>
+
+. $(dirname $0)/DPATCH
+
+exit 0
+@DPATCH@
+diff -urN dovecot-1.0.12/dovecot-example.conf dovecot-1.0.12.debian/dovecot-example.conf
+--- dovecot-1.0.12/dovecot-example.conf	2008-03-04 06:48:12.000000000 +0100
++++ dovecot-1.0.12.debian/dovecot-example.conf	2008-03-06 15:44:02.000000000 +0100
+@@ -21,6 +21,7 @@
+ # Protocols we want to be serving: imap imaps pop3 pop3s
+ # If you only want to use dovecot-auth, you can set this to "none".
+ #protocols = imap imaps
++protocols =
+ 
+ # IP or host address where to listen in for connections. It's not currently
+ # possible to specify multiple addresses. "*" listens in all IPv4 interfaces.
+@@ -70,6 +71,7 @@
+ # Prefix for each line written to log file. % codes are in strftime(3)
+ # format.
+ #log_timestamp = "%b %d %H:%M:%S "
++log_timestamp = "%Y-%m-%d %H:%M:%S "
+ 
+ # Syslog facility to use if you're logging to syslog. Usually if you don't
+ # want to use "mail", you'll use local0..local7. Also other standard
+@@ -89,8 +91,7 @@
+ 
+ # PEM encoded X.509 SSL/TLS certificate and private key. They're opened before
+ # dropping root privileges, so keep the key file unreadable by anyone but
+-# root. Included doc/mkcert.sh can be used to easily generate self-signed
+-# certificate, just make sure to update the domains in dovecot-openssl.cnf
++# root.
+ #ssl_cert_file = /etc/ssl/certs/dovecot.pem
+ #ssl_key_file = /etc/ssl/private/dovecot.pem
+ 
+@@ -201,7 +202,8 @@
+ #   %d - domain part in user@domain, empty if there's no domain
+ #   %h - home directory
+ #
+-# See doc/wiki/Variables.txt for full list. Some examples:
++# See /usr/share/doc/dovecot-common/wiki/Variables.txt for full list. Some
++# examples:
+ #
+ #   mail_location = maildir:~/Maildir
+ #   mail_location = mbox:~/mail:INBOX=/var/mail/%u
+@@ -255,7 +257,7 @@
+ # Group to enable temporarily for privileged operations. Currently this is
+ # used only for creating mbox dotlock files when creation fails for INBOX.
+ # Typically this is set to "mail" to give access to /var/mail.
+-#mail_privileged_group =
++mail_privileged_group = mail
+ 
+ # Grant access to these supplementary groups for mail processes. Typically
+ # these are used to set up access to shared mailboxes. Note that it may be
+@@ -278,8 +280,9 @@
+ # isn't finding your mails.
+ #mail_debug = no
+ 
+-# Log prefix for mail processes. See doc/wiki/Variables.txt for list of
+-# possible variables you can use.
++# Log prefix for mail processes.
++# See /usr/share/doc/dovecot-common/wiki/Variables.txt for list of possible
++# variables you can use.
+ #mail_log_prefix = "%Us(%u): "
+ 
+ # Max. number of lines a mail process is allowed to log per second before it's
+@@ -506,19 +509,19 @@
+ 
+ protocol imap {
+   # Login executable location.
+-  #login_executable = /usr/libexec/dovecot/imap-login
++  #login_executable = /usr/lib/dovecot/imap-login
+ 
+   # IMAP executable location. Changing this allows you to execute other
+   # binaries before the imap process is executed.
+   #
+   # This would write rawlogs into ~/dovecot.rawlog/ directory:
+-  #   mail_executable = /usr/libexec/dovecot/rawlog /usr/libexec/dovecot/imap
++  #   mail_executable = /usr/lib/dovecot/rawlog /usr/lib/dovecot/imap
+   #
+   # This would attach gdb into the imap process and write backtraces into
+   # /tmp/gdbhelper.* files:
+   #   mail_executable = /usr/libexec/dovecot/gdbhelper /usr/libexec/dovecot/imap
+   #
+-  #mail_executable = /usr/libexec/dovecot/imap
++  #mail_executable = /usr/lib/dovecot/imap
+ 
+   # Maximum IMAP command line length in bytes. Some clients generate very long
+   # command lines with huge mailboxes, so you may need to raise this if you get
+@@ -528,7 +531,7 @@
+   # Support for dynamically loadable plugins. mail_plugins is a space separated
+   # list of plugins to load.
+   #mail_plugins = 
+-  #mail_plugin_dir = /usr/lib/dovecot/imap
++  #mail_plugin_dir = /usr/lib/dovecot/modules/imap
+ 
+   # Send IMAP capabilities in greeting message. This makes it unnecessary for
+   # clients to request it with CAPABILITY command, so it saves one round-trip.
+@@ -571,11 +574,11 @@
+ 
+ protocol pop3 {
+   # Login executable location.
+-  #login_executable = /usr/libexec/dovecot/pop3-login
++  #login_executable = /usr/lib/dovecot/pop3-login
+ 
+   # POP3 executable location. See IMAP's mail_executable above for examples
+   # how this could be changed.
+-  #mail_executable = /usr/libexec/dovecot/pop3
++  #mail_executable = /usr/lib/dovecot/pop3
+ 
+   # Don't try to set mails non-recent or seen with POP3 sessions. This is
+   # mostly intended to reduce disk I/O. With maildir it doesn't move files
+@@ -620,7 +623,7 @@
+   # installations. %08Xu%08Xv will be the new default, so use it for new
+   # installations.
+   #
+-  #pop3_uidl_format = 
++  pop3_uidl_format = %08Xu%08Xv
+ 
+   # POP3 logout format string:
+   #  %t - number of TOP commands
+@@ -635,7 +638,7 @@
+   # Support for dynamically loadable plugins. mail_plugins is a space separated
+   # list of plugins to load.
+   #mail_plugins = 
+-  #mail_plugin_dir = /usr/lib/dovecot/pop3
++  #mail_plugin_dir = /usr/lib/dovecot/modules/pop3
+ 
+   # Workarounds for various client bugs:
+   #   outlook-no-nuls:
+@@ -652,9 +655,9 @@
+ ## LDA specific settings
+ ##
+ 
+-protocol lda {
++# protocol lda {
+   # Address to use when sending rejection mails.
+-  postmaster_address = postmaster@example.com
++  # postmaster_address = postmaster@example.com
+ 
+   # Hostname to use in various parts of sent mails, eg. in Message-Id.
+   # Default is the system's real hostname.
+@@ -663,21 +666,24 @@
+   # Support for dynamically loadable plugins. mail_plugins is a space separated
+   # list of plugins to load.
+   #mail_plugins = 
+-  #mail_plugin_dir = /usr/lib/dovecot/lda
++  #mail_plugin_dir = /usr/lib/dovecot/modules/lda
+ 
+   # Binary to use for sending mails.
+   #sendmail_path = /usr/lib/sendmail
+ 
+   # UNIX socket path to master authentication server to find users.
+   #auth_socket_path = /var/run/dovecot/auth-master
+-}
++
++  # Enabling Sieve plugin for server-side mail filtering
++  # mail_plugins = cmusieve
++# }
+ 
+ ##
+ ## Authentication processes
+ ##
+ 
+ # Executable location
+-#auth_executable = /usr/libexec/dovecot/dovecot-auth
++#auth_executable = /usr/lib/dovecot/dovecot-auth
+ 
+ # Set max. process size in megabytes.
+ #auth_process_size = 256
+@@ -813,8 +819,8 @@
+     # because PAM modules can do all kinds of checks besides checking password,
+     # such as checking IP address. Dovecot can't know about these checks
+     # without some help. cache_key is simply a list of variables (see
+-    # doc/wiki/Variables.txt) which must match for the cached data to be used.
+-    # Here are some examples:
++    # /usr/share/doc/dovecot-common/wiki/Variables.txt) which must match for
++    # the cached data to be used. Here are some examples:
+     #   %u - Username must match. Probably sufficient for most uses.
+     #   %u%r - Username and remote IP address must match.
+     #   %u%s - Username and service (ie. IMAP, POP3) must match.
+@@ -868,14 +874,14 @@
+ 
+   # SQL database <doc/wiki/AuthDatabase.SQL.txt>
+   #passdb sql {
+-    # Path for SQL configuration file, see doc/dovecot-sql-example.conf
+-    #args = 
++    # Path for SQL configuration file
++    #args = /etc/dovecot/dovecot-sql.conf
+   #}
+ 
+   # LDAP database <doc/wiki/AuthDatabase.LDAP.txt>
+   #passdb ldap {
+-    # Path for LDAP configuration file, see doc/dovecot-ldap-example.conf
+-    #args = 
++    # Path for LDAP configuration file
++    #args = /etc/dovecot/dovecot-ldap.conf
+   #}
+ 
+   # vpopmail authentication <doc/wiki/AuthDatabase.VPopMail.txt>
+@@ -929,14 +935,14 @@
+ 
+   # SQL database <doc/wiki/AuthDatabase.SQL.txt>
+   #userdb sql {
+-    # Path for SQL configuration file, see doc/dovecot-sql-example.conf
+-    #args = 
++    # Path for SQL configuration file
++    #args = /etc/dovecot/dovecot-sql.conf
+   #}
+ 
+   # LDAP database <doc/wiki/AuthDatabase.LDAP.txt>
+   #userdb ldap {
+-    # Path for LDAP configuration file, see doc/dovecot-ldap-example.conf
+-    #args = 
++    # Path for LDAP configuration file
++    #args = /etc/dovecot/dovecot-ldap.conf
+   #}
+ 
+   # vpopmail <doc/wiki/AuthDatabase.VPopMail.txt>
+@@ -995,6 +1001,18 @@
+       #mode = 0660
+     #}
+   #}
++
++  ## dovecot-lda specific settings
++  ##
++  # socket listen {
++  #   master {
++  #     path = /var/run/dovecot/auth-master
++  #     mode = 0600
++  #     user = mail # User running Dovecot LDA
++  #     #group = mail # Or alternatively mode 0660 + LDA user in this group
++  #   }
++  # }
++
+ }
+ 
+ # If you wish to use another authentication server than dovecot-auth, you can
--- dovecot-1.0.15.orig/debian/patches/dovecot-1.0.15-managesieve-v9.3-security.dpatch
+++ dovecot-1.0.15/debian/patches/dovecot-1.0.15-managesieve-v9.3-security.dpatch
@@ -0,0 +1,43 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## dovecot-1.0.15-managesieve-v9.3-security.dpatch by
+## Stephan Bosch <stephan@rename-it.nl>
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: fix security hole in managesieve: virtual users can edit scripts
+## DP: of other virtual users
+
+@DPATCH@
+diff -uNr --exclude=.hg dovecot-1.0.old/src/lib-sievestorage/sieve-save.c dovecot-1.0/src/lib-sievestorage/sieve-save.c
+--- dovecot-1.0.old/src/lib-sievestorage/sieve-save.c	2008-11-17 16:45:54.000000000 +0100
++++ dovecot-1.0/src/lib-sievestorage/sieve-save.c	2008-11-17 16:44:17.000000000 +0100
+@@ -154,6 +154,13 @@
+ 	struct ostream *output;
+ 	const char *path;
+ 
++	/* Disallow '/' characters in script name */
++	if ( strchr(scriptname, '/') != NULL ) {
++		sieve_storage_set_error(storage, "Invalid script name '%s'.",
++			scriptname);
++		return NULL;
++	}
++
+ 	/* Prevent overwriting the active script link when it resides in the 
+ 	 * sieve storage directory.
+ 	 */
+diff -uNr --exclude=.hg dovecot-1.0.old/src/lib-sievestorage/sieve-script.c dovecot-1.0/src/lib-sievestorage/sieve-script.c
+--- dovecot-1.0.old/src/lib-sievestorage/sieve-script.c	2008-11-17 16:45:54.000000000 +0100
++++ dovecot-1.0/src/lib-sievestorage/sieve-script.c	2008-11-17 16:44:17.000000000 +0100
+@@ -93,6 +93,13 @@
+ 	struct sieve_script *script;
+ 	const char *filename;
+ 
++	/* Disallow '/' characters in script name */
++	if ( strchr(scriptname, '/') != NULL ) {	
++		sieve_storage_set_error(storage, "Invalid script name '%s'.",
++			scriptname);
++		return NULL;
++	}
++
+ 	t_push();
+ 
+ 	filename = t_strconcat
--- dovecot-1.0.15.orig/debian/patches/dovecot-drac.dpatch
+++ dovecot-1.0.15/debian/patches/dovecot-drac.dpatch
@@ -0,0 +1,79 @@
+#! /bin/sh -e
+
+## DP: Include drac module
+## DP: Author: Fabio Tranchitella <kobold@debian.org>
+
+. $(dirname $0)/DPATCH
+
+exit 0
+@DPATCH@
+diff -urN dovecot-1.0.beta3/src/drac/drac.c dovecot-1.0.beta3.debian/src/drac/drac.c
+--- dovecot-1.0.beta3/src/drac/drac.c	1970-01-01 00:00:00.000000000 +0000
++++ dovecot-1.0.beta3.debian/src/drac/drac.c	2006-02-14 08:36:58.000000000 +0000
+@@ -0,0 +1,60 @@
++/* Copyright (C) 2003 Timo Sirainen */
++
++/* Installation:
++
++   export dovecot=~/src/dovecot
++
++   gcc -Wall -W -shared -fPIC -DHAVE_CONFIG_H -I$dovecot -I$dovecot/src/lib \
++   drac.c -o drac.so -ldrac
++
++   cp drac.so /usr/local/lib/dovecot/imap/
++   cp drac.so /usr/local/lib/dovecot/pop3/
++*/
++
++#include "lib.h"
++#include "ioloop.h"
++#include "network.h"
++
++#include <stdlib.h>
++
++#define DRAC_TIMEOUT_SECS 60
++#define DRAC_HOST "localhost"
++
++int dracauth(char *host, unsigned long ip, char **errmsg);
++
++static struct timeout *to_drac = NULL;
++static unsigned long in_ip;
++
++static void drac_timeout(void *context __attr_unused__)
++{
++	char *err;
++
++	if (dracauth(DRAC_HOST, in_ip, &err) != 0)
++		i_error("dracauth() failed: %s", err);
++}
++
++void drac_init(void)
++{
++	const char *ip_str;
++	struct ip_addr ip;
++
++	ip_str = getenv("IP");
++	if (ip_str == NULL)
++		i_error("DRAC: IP environment not given");
++	else if (net_addr2ip(ip_str, &ip) < 0)
++		i_error("DRAC: net_ip2addr(%s) failed: %m", ip_str);
++	else if (!IPADDR_IS_V4(&ip))
++		i_error("DRAC: Only IPv4 addresses are supported (%s)", ip_str);
++	else {
++		in_ip = ((struct in_addr *) &ip.ip)->s_addr;
++		drac_timeout(NULL);
++		to_drac = timeout_add(1000*DRAC_TIMEOUT_SECS,
++				      drac_timeout, NULL);
++	}
++}
++
++void drac_deinit(void)
++{
++	if (to_drac != NULL)
++		timeout_remove(&to_drac);
++}
+diff -urN dovecot-1.0.beta3/src/drac/Makefile dovecot-1.0.beta3.debian/src/drac/Makefile
+--- dovecot-1.0.beta3/src/drac/Makefile	1970-01-01 00:00:00.000000000 +0000
++++ dovecot-1.0.beta3.debian/src/drac/Makefile	2006-02-17 17:20:59.000000000 +0000
+@@ -0,0 +1,2 @@
++all:
++	gcc -Wall -W -shared -fPIC -DHAVE_CONFIG_H -I ../.. -I../lib drac.c -o drac.so -ldrac
--- dovecot-1.0.15.orig/debian/patches/exec_check_for_none.dpatch
+++ dovecot-1.0.15/debian/patches/exec_check_for_none.dpatch
@@ -0,0 +1,22 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+
+@DPATCH@
+diff -urNad dovecot-1.0.10~/src/master/master-settings.c dovecot-1.0.10/src/master/master-settings.c
+--- dovecot-1.0.10/src/master/master-settings.c	2007-12-21 16:10:24.000000000 +0100
++++ dovecot-1.0.10/src/master/master-settings.c.debian	2007-12-30 10:46:02.000000000 +0100
+@@ -638,6 +638,7 @@
+ 	}
+ 
+ 	if (set->protocol != MAIL_PROTOCOL_ANY &&
++        strcmp(set->protocols, "none") &&
+ 	    access(t_strcut(set->mail_executable, ' '), X_OK) < 0) {
+ 		i_error("Can't use mail executable %s: %m",
+ 			t_strcut(set->mail_executable, ' '));
+@@ -716,6 +717,7 @@
+ 	}
+ 
+ 	if (set->protocol != MAIL_PROTOCOL_ANY &&
++        strcmp(set->protocols, "none") &&
+ 	    access(t_strcut(set->login_executable, ' '), X_OK) < 0) {
+ 		i_error("Can't use login executable %s: %m",
+ 			t_strcut(set->login_executable, ' '));
--- dovecot-1.0.15.orig/debian/patches/quota_v2.dpatch
+++ dovecot-1.0.15/debian/patches/quota_v2.dpatch
@@ -0,0 +1,70 @@
+#! /bin/sh -e
+
+## DP: Hack to support quota v2, Based heavily on email found here:
+## DP: http://dovecot.org/list/dovecot/2006-June/014015.html
+## DP: Author: Jonas Smedegaard <dr@jones.dk>
+
+. $(dirname $0)/DPATCH
+
+exit 0
+@DPATCH@
+diff -ruN dovecot-1.0.rc2.orig/src/plugins/quota/quota-fs.c dovecot-1.0.rc2/src/plugins/quota/quota-fs.c
+--- dovecot-1.0.rc2.orig/src/plugins/quota/quota-fs.c	2006-07-01 22:13:10.000000000 +0200
++++ dovecot-1.0.rc2/src/plugins/quota/quota-fs.c	2006-07-10 05:03:52.000000000 +0200
+@@ -177,6 +177,21 @@
+ 		      uint64_t *value_r, uint64_t *limit_r)
+ {
+ 	struct fs_quota_root *root = (struct fs_quota_root *)_root;
++#ifndef _LINUX_QUOTA_VERSION
++	struct fake_dq_struct {
++		u_int64_t dqb_bhardlimit;   /* absolute limit on disk quota blocks alloc */
++		u_int64_t dqb_bsoftlimit;   /* preferred limit on disk quota blocks */
++		u_int64_t dqb_curblocks;    /* current quota block count */
++		u_int64_t dqb_ihardlimit;   /* maximum # allocated inodes */
++		u_int64_t dqb_isoftlimit;   /* preferred inode limit */
++		u_int64_t dqb_curinodes;    /* current # allocated inodes */
++		u_int64_t dqb_btime;        /* time limit for excessive disk use */
++		u_int64_t dqb_itime;        /* time limit for excessive files */
++		u_int32_t dqb_valid;        /* bitmask of QIF_* constants */
++	};
++
++	struct fake_dq_struct fake_dqblk;
++#endif
+ 	struct dqblk dqblk;
+ #ifdef HAVE_Q_QUOTACTL
+ 	struct quotctl ctl;
+@@ -210,8 +225,25 @@
+ 		*limit_r = xdqblk.d_blk_softlimit >> 1;
+ 	} else
+ #endif
++#ifndef _LINUX_QUOTA_VERSION
++	{
++		/* ext2, ext3; quota v2 */
++		if (quotactl(0x80000700,
++			     root->mount->device_path,
++			     root->uid, (caddr_t)&fake_dqblk) < 0) {
++			i_error("quotactl(Q_GETQUOTA, %s) failed: %m",
++				root->mount->device_path);
++			quota_set_error(_root->setup->quota,
++					"Internal quota error");
++			return -1;
++		}
++
++		*value_r = fake_dqblk.dqb_curblocks / 1024;
++		*limit_r = fake_dqblk.dqb_bsoftlimit;
++	}
++#else
+ 	{
+-		/* ext2, ext3 */
++		/* ext2, ext3; quota v1 */
+ 		if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA),
+ 			     root->mount->device_path,
+ 			     root->uid, (caddr_t)&dqblk) < 0) {
+@@ -225,6 +257,7 @@
+ 		*value_r = dqblk.dqb_curblocks / 1024;
+ 		*limit_r = dqblk.dqb_bsoftlimit;
+ 	}
++#endif
+ #elif defined(HAVE_QUOTACTL)
+ 	/* BSD, AIX */
+ 	if (quotactl(root->mount->mount_path, QCMD(Q_GETQUOTA, USRQUOTA),
--- dovecot-1.0.15.orig/debian/dovecot-common.README.Debian
+++ dovecot-1.0.15/debian/dovecot-common.README.Debian
@@ -0,0 +1,531 @@
+Package configuration options
+-----------------------------
+
+The following options were used when configuring dovecot:
+
+ * LDAP support
+ * MySQL support
+ * Postgresql support
+ * OpenSSL as the SSL library
+ * Included dovecot-lda (please check the following url for documentation:
+   http://wiki.dovecot.org/LDA#head-09168e12c3cecac1f48551245bde0dd07663429f)
+
+
+(Extracted from: http://wiki.dovecot.org/)
+
+Where is dovecot-example.conf?
+------------------------------
+This file is mentioned in the documentation. Where is it?  In this Debian
+package it is used as the default configuration file /etc/dovecot/dovecot.conf
+
+
+Configuring Dovecot or where do i store my mails?
+-------------------------------------------------
+
+In /etc/dovecot/dovecot.conf, the default mail location is set using the
+mail_location setting. This is the new setting which replaces the old
+default_mail_env from earlier versions. You can use some variables in the value:
+
+%u - full username 
+%n - user part in user@domain, same as %u if there's no domain 
+%d - domain part in user@domain, empty if there's no domain 
+%h - home directory 
+
+
+Typically with maildir this would be set to: 
+
+mail_location = maildir:~/Maildir
+
+or with mbox: 
+
+mail_location = mbox:~/mail:INBOX=/var/mail/%u
+
+Index files are by default stored under the same directory as mails.
+With maildir they are stored in the actual maildirs, with mbox they are
+stored under .imap/ directory. You can change these by adding
+:INDEX=location to location string. For example:
+
+mail_location = mbox:%h/mail:INBOX=/var/mail/%u:INDEX=%h/indexes
+
+If you didn't set home directory, %h can't be used. Instead you can do
+something like:
+
+mail_location = maildir:/home/%u/Maildir
+
+With virtual users the mail and home directories are probably the same.
+In that case you would just do:
+
+mail_location = maildir:%h
+
+
+For more details on the mail_location setting, please have a look at:
+http://wiki.dovecot.org/MailLocation
+
+
+Migrating to Dovecot or how can i recycle my <insert funky mailerhere> mails?
+------------------------------------------------------------------------------
+
+1. Migration to Dovecot
+
+When migrating from one IMAP server to another, you should make sure
+that these are preserved: Mailbox subscription list User would be able
+to manually subscribe them again if you don't want to mess with it. 
+Message UIDs If UIDs are lost, at the minimum clients' message cache
+gets cleaned Some IMAP clients store metadata by assigning it to
+specific UID, if UIDs are changed these will be lost. Message flags
+Lost flags can be really annoying, you most likely want to avoid it. 
+Here's the more server-specific instructions:
+
+2. UW-IMAP
+
+By default UW-IMAP allows access to whole home directory, and many
+people have chosen to store their mails in mail/ directory. This usually
+means that IMAP clients have set "IMAP namespace" to "mail/", which
+doesn't work well with Dovecot, as Dovecot by default uses mail/
+directory directly. So if IMAP namespace is kept as "mail/", Dovecot
+would try to access "~/mail/mail/" directory. So, removing the prefix
+from IMAP clients would be the first step. Next problem is that
+subscribed mailboxes are listed as "mail/box" or "~/mail/box" or
+"~user/mail/box" in subscriptions file. You'd have to remove the mail/
+directory part from all of these. The subscriptions file name is also
+different, UW-IMAP uses .mailboxlist while Dovecot uses .subscriptions. 
+Dovecot uses UW-IMAP compatible UID and message flag headers in mboxes,
+so that's not a problem. 
+
+Settings: 
+
+mail_location = mbox:~/mail:INBOX=/var/mail/%u
+# make sure mbox_locks are the same with all software that accesses your mboxes
+mbox_locks = dotlock fcntl
+
+If you want to make a transparent migration to Dovecot without having to
+change the configuration on hundreds of client systems, you will need a
+slightly different configuration. If your clients have the server
+prefix set to something like "~/mail", this will not work unless you
+enable mail_full_filesystem_access in your Dovecot configuration.
+Dovecot will otherwise reject mailbox names or prefixes that start with
+"~". You can either rename your ".mailboxlist" file to ".subscriptions"
+for all you users, or change the definition of SUBSCRIPTION_FILE_NAME in
+src/lib-storage/subscription-file/subscription-file.c. If ~/mbox file
+exists, UW-IMAP moves mails from /var/mail/user there. Currently Dovecot
+doesn't support this feature, so you'll have to either move everyone's
+mails to ~/mbox and reconfigure MTA/LDA to store mails there, or
+alternatively get the ~/mbox users to move their mails back to
+/var/mail/user. This feature may be implemented later, but it's not
+planned in near future. 
+
+3. UW-POP3
+
+Dovecot generates POP3 UIDs differently than UW-IMAP. You most likely
+want to preserve them, so currently you'll have to patch Dovecot
+(http://dovecot.org/patches/pop3-uidl-uwimap.patch).
+
+4. Courier
+
+Courier by default uses "INBOX." as private IMAP namespace, so it has
+exactly the same problems as described with UW-IMAP above. Courier's
+courierimapsubscribed is compatible with Dovecot's .subscriptions file,
+just rename it and remove the "INBOX." prefixes. Courier's
+courierimapuiddb is compatible with Dovecot's dovecot-uidlist file, just
+rename it. Courier's message flags are compatible with Dovecot (as it's
+specified by Maildir specification) Courier's message keywords
+implementation isn't Dovecot compatible and there's currently no easy
+way to migrate them. 
+
+Settings: 
+
+# normal home directories
+mail_location = maildir:~/Maildir
+# for virtual users
+mail_location = maildir:~/
+
+5. Cyrus
+
+See cyrus2courier (http://madness.at/projects/), it's
+Dovecot-compatible. Also mirrored at dovecot.org
+(http://dovecot.org/tools/).
+
+6. Other POP3 servers
+
+Different POP3 servers generate UIDs differently. If you want to
+preserve them to avoid users downloading their mails twice, you'll need
+to figure out how the server generates the UID and patch Dovecot
+accordingly to do the same. In future Dovecot will support reading the
+UID from X-UIDL header, and if it doesn't exist it will use it's own
+method. This feature is almost there, but not quite yet. Here is a list
+of POP3 servers and how they generate their UIDs. Please update if you
+know more:
+
+popa3d (http://www.openwall.com/popa3d/) Generates MD5 sum from a couple
+of headers. Dovecot uses compatible MD5 sums internally.
+
+
+Logging to file rather than via syslog
+--------------------------------------
+
+If you'd like to log directly to files rather than via syslog, the 
+following may help:
+
+In /etc/dovecot/dovecot.conf set the following variables:
+log_path: /var/log/dovecot/error.log
+info_log_path: /var/log/dovecot/info.log
+
+Create the dovecot log directory and set the ownership and permissions 
+appropriately
+
+mkdir /var/log/dovecot
+chown dovecot.adm /var/log/dovecot 
+chmod 2755 /var/log/dovecot
+
+# Save this as /etc/logrotate.d/dovecot
+/var/log/dovecot/error.log /var/log/dovecot/info.log {
+        daily
+        missingok
+        rotate 60
+        compress
+        delaycompress
+        notifempty
+        create 640 dovecot adm
+        sharedscripts
+        postrotate
+                if [ -f /var/run/dovecot/master.pid ]; then
+                        /bin/kill -USR1 `cat /var/run/dovecot/master.pid`
+                fi
+        endscript
+}
+
+
+Question and Answers
+--------------------
+
+1. Does Dovecot support a single user with a mixture of mail storage formats?
+   For example, Maildir for INBOX and unix mailbox for older archives. 
+
+http://dovecot.org/list/dovecot/2004-August/004353.html 
+http://wiki.dovecot.org/MailLocation
+
+"It's possible with 1.0-tests by creating separate namespaces for INBOX
+and others. Not possible with 0.99 though." 
+
+
+2. Do all users need to use the same format of mail storage?
+
+mail_location can be overridden in userdb for each user. This works
+with userdbs supporting the "mail" attribute (eg. passwd-file, SQL,
+LDAP). If you don't set mail_location at all, Dovecot attempts to do
+automatic detection. In that case it allows either maildir in ~/Maildir
+or mbox in ~/mail or ~/Mail. In future perhaps there will also be
+per-user ~/.dovecotrc which allows specifying where the mails are
+located. 
+
+3. How do I setup vpopmail auth in dovecot.conf ?
+
+The Debian package doesn't include vpopmail support. So you will have
+to rebuild it.
+
+ * You should be sure that "./configure" found vpopmail. When finished,
+   configure shows a summary. You should notice that vpopmail is available
+   as auth module. 
+
+ * If you've compiled vpopmail whith --prefix=/var/vpopmail, 
+   /etc/dovecot/dovecot.conf should look like:
+
+   auth_userdb = vpopmail
+   auth_passdb = vpopmail
+   mail_location = maildir:/var/vpopmail/domains/%d/%n/Maildir
+
+4. How do I set the inbox-path in Pine?
+
+The comments in .pinerc suggest the following for reading mail on a
+remote server: 
+inbox-path={carsen.u.washington.edu}INBOX 
+
+Change "carsen.u.washington.edu" to whatever is appropriate for your setup. If
+your IMAP server supports SSL or TLS, append "/ssl" or "/tls" to the
+server name, for example "carsen.u.washington.edu/tls". Another option
+is to include the username using the "/user=UID" qualifier, for example
+"carsen.u.washington.edu/ssl/user=timo". 
+
+5. How do I set the IMAP Mailbox Location Prefix in Eudora?
+
+Leave it blank. 
+
+6. How do I set the folder location in Mutt?
+
+ * edit .muttrc
+ 
+   set spoolfile=imap://user@hostname/INBOX
+   set folder=imap://user@hostname/
+
+ * problems
+   mutt still helpfully offers to: 
+
+   Move read messages to /home/$user/mbox? ([no]/yes):
+   Some .muttrc options are: 
+   don't ask about moving messages, just do it: 
+   set move=yes
+   don't ask about moving messages and _don't_ do it: 
+   set move=no
+   ask about moving message, default answer is yes: 
+   set move=ask-yes
+   ask about moving message, default answer is no: 
+   set move=ask-no
+
+
+ * References
+
+   http://mutt.sourceforge.net/imap/ 
+   http://jamespo.org.uk/blog/archives/000271.html
+
+
+7. Why isn't raw logging working?
+
+ * You haven't configured raw logging in /etc/dovecot/dovecot.conf.
+   See the comments in the "protocol imap" section of /etc/dovecot/dovecot.conf
+   for more information.
+
+ * Your user database doesn't specify the home directory for the user.
+   Dovecot doesn't know where to put raw logs if the user database doesn't
+   tell it. 
+
+ * If you are using LDAP, the user_attrs setting in dovecot-ldap.conf
+   doesn't specify homeDirectory. Dovecot will only pull attributes from
+   the LDAP records if they are listed in this setting. 
+
+ * You don't have the dovecot.rawlog directory in the user's home
+   directory. Dovecot will post rawlog entries only if this directory is
+   present. 
+
+ * You have the dovecot.rawlog directory in the wrong directory. 
+
+8. Why can't I change the log location?
+
+Dovecot doesn't change its log location if you change the config file
+and send the SIGHUP signal with one of the following
+
+kill -1 <pid>
+kill -HUP <pid>
+
+You have to shutdown Dovecot and restart it: 
+
+invoke-rc.d dovecot restart
+
+9. Why can't users access their mail?
+
+Try connecting from the command line. 
+telnet <mailserver> imap2
+
+ * Dovecot isn't running.
+
+  Start Dovecot. 
+
+ * Dovecot is running.
+
+ The output from the connection attempt is:
+
+ Connected to mailserver
+ Escape character is '^]'.
+ * Dovecot ready
+
+ Try logging in from the command line:
+
+ 1 LOGIN <username> <password>
+
+ * Users can't LOGIN.
+
+  The output from the login attempt is:
+
+  1 NO Authentication failed
+
+  Possible reasons: 
+
+    * The user isn't in your user database. 
+
+    * The user is in your user database, but there's no password listed. 
+
+    * If you are using LDAP, the pass_attrs setting in dovecot-ldap.conf
+      doesn't specify password. 
+
+    * You mispelled the user name or the password. 
+
+    * You typed the wrong password. 
+
+    Hint: set "auth_verbose = yes" in dovecot.conf for more information. 
+
+  The output from the login attempt is 
+
+  1 NO Login failed: Unsupported authentication mechanism
+
+  Possible reasons: 
+
+    * You don't have "auth_mechanisms = plain" for any of your
+      authentication processes. (The above suggested LOGIN command uses plain
+      text authentication.) 
+
+  * Users can LOGIN, but they can't SELECT.
+    The output from the login attempt is :
+
+    1 OK logged in
+
+    but the output from 
+
+    2 SELECT <mailbox> 
+
+    is 
+
+    NO Internal error [<date> <time>]
+
+  Possible reasons: 
+
+  The user database contains a UID number for the user that does not match
+  the owner of the files the mail is stored in. If you are using LDAP,
+  dovecot-ldap.conf contains a default uid setting that doesn't match the
+  owner of the files the mail is stored in, and the user record in the
+  user database doesn not contain a UID number. The user's mail files are
+  not in the location specified in the dovecot.conf mail_location
+  setting. Under Debian Stable (woody) if /var/mail is owned by root and
+  group mail, the defaults, the permissions must be drwxrwxrwt or you will
+  get: file_lock_dotlock() failed with mbox file /var/mail/user:
+  Permission denied in /var/log/syslog.
+
+10. Why isn't Dovecot listening on localhost (127.0.0.1)?
+
+If you specify an interface in dovecot.conf, Dovecot may listen only at
+that interface and not at localhost. Set:
+
+imap_listen = *
+
+Shut down Dovecot completely, and restart it.
+
+invoke-rc.d dovecot restart
+
+11. Nothing I do works! I'm losing my sanity! Give me some clues!
+
+I'm sorry. There are no sanity clues. 
+
+12. What are the problems mentioned with Hard Quotas?
+
+Commands may fail with "Internal error" messages and users may not be
+able to even open their mailboxes. This will probably be fixed before
+v1.0, but there will always be some theoretical problems that can't be
+solved (if message UIDs can't be saved, how is it possible to ensure
+same UID doesn't point to different messages at different times?). 
+
+13. Why is everything in the maildir created with group 1000?
+
+Logged in user's group ID is 1000 
+
+14. Will Dovecot detect a new account simply by adding the mail folders?
+
+For each user Dovecot needs to verify their password and find their mail
+directory. With maildir you need to do mkdir ~user/Maildir, with mbox
+mkdir ~user/mail. Your system may use /etc/skel as a template when
+creating a new system user, so you could mkdir /etc/skel/Maildir or
+/etc/skel/mail as approriate. I believe that in the default
+configuration Dovecot should automatically allow system users to connect
+to the mail server. It is possible to add virtual mail users, ie the
+Dovecot recognizes them as a user but they are not a real system user.
+
+15. Can Dovecot authenticate and work via UNIX sockets?
+
+Dovecot authentication already works via UNIX sockets, but it only
+speaks its internal protocol. You could always create a "socket"
+passdb/userdb. Probably should be made compatible with "checkpassword"
+protocol. Patches welcome
+
+16. Why isn't the POP3 service running?
+
+It needs to be added into protocols line in configuration file.
+
+
+Troubleshooting
+---------------
+
+1. Missing mailboxes
+
+Dovecot by default doesn't use any "personal IMAP namespace prefix",
+which clients often call either "IMAP namespace" or "IMAP prefix". With
+Courier you probably had this set to "INBOX.", with UW-IMAP you might
+have set it to "mail/". So, the solution is simply to set this field
+empty and restart your IMAP client. If this didn't help, you might have
+mail_location setting wrong. If it's unset, Dovecot tries to detect
+where your mail is stored by looking at ~/Maildir, ~/mail,
+/var/spool/mail/ and /var/mail/ directories. Depending on what you want,
+Dovecot might have guessed wrong. If that didn't help, make sure the
+directory permissions are correct. 
+
+2. Missing INBOX
+
+See if the mails are stored in ~/mbox file. If ~/mbox file exists,
+UW-IMAP moves mails from /var/mail/user there. Currently Dovecot doesn't
+support this feature, so you'll have to either move everyone's mails to
+~/mbox and reconfigure MTA/LDA to store mails there, or alternatively
+get the ~/mbox users to move their mails back to /var/mail/user. This
+feature may be implemented later, but it's not planned in near future. 
+
+3. Subscriptions
+
+Dovecot uses different filenames for list of mailbox subscriptions.
+You'll need to rename these to ones that Dovecot wants (currently
+".subscriptions"). See Migration page for more information. 
+
+4. Troubleshooting
+
+If it's still not working, check first if the problem is with IMAP
+client or server configuration. Easiest way to do this is to talk IMAP
+directly:
+
+telnet imap.server.org 143
+x login username password
+x list "" *
+
+If you see a list of expected mailboxes, the problem is with your IMAP client.
+
+5. Internal Errors
+
+Dovecot doesn't usually send actual error messages to client. Instead
+clients see: Internal error occured. Error report written to server log.
+[2004-08-23 11:23:01] The point is that whenever anything unexpected
+happens, we don't want to leak any extra information about it to
+clients. They don't need it and they might try to exploit it in some
+ways, so the less they know the better. The real error messages is
+written to log file where system administrator can figure out what's
+wrong and fix it. The timestamp is meant to be sent by user to sysadmin
+while reporting the problem, so it's easier to find the real error
+message from the log file. 
+
+Oct  9 23:26:10 kwai dovecot: login: fd_read() couldn't read all req
+Oct  9 23:26:36 kwai last message repeated 442 times
+
+I saw the above error message when I accidently installed 0.99.11-3
+packages and upgraded only imapd to 1.0-test46. Debian dependencies
+should have prevented the problem, but I had unwittingly worked around
+them.
+
+6. open(/var/mail/user.lock) failed: Permission denied
+
+Error: open(/var/mail/user.lock) failed: Permission denied
+Error: file_lock_dotlock() failed with mbox file /var/mail/user: Permission 
+denied
+
+Since v0.99.10.9 it's possible to fix this by adding to config file: 
+
+mail_extra_groups = mail
+
+or whatever group owns your /var/mail directory. Keeping sticky bit
+(chmod +t) for /var/mail might still be a good idea for extra security.
+
+7. Couldn't open INBOX: Internal error occurred.
+
+The setting mail_location describes the location for users' mailboxes.
+The default is empty, which means that Dovecot tries to find the mailboxes
+automatically. This won't work if the user doesn't have any mail yet, so you
+should explicitly tell Dovecot the full location.
+
+If you're using mbox, giving a path to the INBOX file (eg. /var/mail/%u)
+isn't enough. You'll also need to tell Dovecot where the other mailboxes are
+and where Dovecot can place its index files. This is called the "root mail
+directory", and it must be the first path given in the mail_location setting.
+
+See the "Configuring Dovecot or where do i store my mails?" paragraph in this
+file fore a more detailed explanation.
--- dovecot-1.0.15.orig/debian/rules
+++ dovecot-1.0.15/debian/rules
@@ -0,0 +1,136 @@
+#!/usr/bin/make -f
+# Sample debian/rules that uses debhelper.
+# GNU copyright 1997 to 1999 by Joey Hess.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+# These are used for cross-compiling and for saving the configure script
+# from having to guess our platform (since we know it already)
+DEB_HOST_GNU_TYPE   ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_BUILD_GNU_TYPE  ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+
+
+ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS)))
+	CFLAGS += -g
+endif
+ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
+	INSTALL_PROGRAM += -s
+endif
+
+config.status: patch-stamp configure
+	dh_testdir
+	-test -r /usr/share/misc/config.sub && \
+	  cp -f /usr/share/misc/config.sub config.sub
+	-test -r /usr/share/misc/config.guess && \
+	  cp -f /usr/share/misc/config.guess config.guess
+	aclocal-1.9; automake-1.9 --add-missing; libtoolize -f -c;  aclocal-1.9; autoconf
+	# Add here commands to configure the package.
+	./configure --with-ldap \
+	            --with-ssl=openssl \
+	            --with-pgsql \
+	            --with-mysql \
+	            --with-sqlite \
+	            --with-cyrus-sasl2 \
+	            --with-gssapi \
+	            --with-deliver \
+	            --with-ioloop=best \
+	            --enable-header-install \
+	            --host=$(DEB_HOST_GNU_TYPE) \
+	            --build=$(DEB_BUILD_GNU_TYPE) \
+	            --prefix=/usr \
+	            --sysconfdir=/etc/dovecot \
+	            --libexecdir=\$${prefix}/lib \
+	            --localstatedir=/var \
+	            --mandir=\$${prefix}/share/man \
+	            --infodir=\$${prefix}/share/info \
+	            --with-moduledir=\$${prefix}/lib/dovecot/modules
+
+build: build-stamp
+
+
+build-stamp: config.status
+	dh_testdir
+
+	# Add here commands to compile the package.
+	$(MAKE)
+	# dovecot-sieve plugin
+	cd dovecot-sieve && sh autogen.sh
+	cd dovecot-sieve && ./configure --with-dovecot=.. \
+		--prefix=/usr \
+		--libexecdir=\$${prefix}/lib \
+		--with-moduledir=\$${prefix}/lib/dovecot/modules
+	$(MAKE) -C dovecot-sieve
+	touch build-stamp
+
+clean: clean1 unpatch
+clean1:
+	dh_testdir
+	dh_testroot
+	rm -f build-stamp 
+	# Add here commands to clean up after the build process.
+	[ ! -f Makefile ] || $(MAKE) distclean
+	[ ! -f dovecot-sieve/Makefile ] || $(MAKE) -C dovecot-sieve distclean
+	rm -f conftest conftest.o config.log config.cache config.status ylwrap
+	[ -f config.sub.orig ] && mv config.sub.orig config.sub || true
+	[ -f config.guess.orig ] && mv config.sub.orig config.guess || true
+	dh_clean
+
+install: build
+	dh_testdir
+	dh_testroot
+	dh_clean -k
+	dh_installdirs
+
+	# Add here commands to install the package into debian/dovecot.
+	$(MAKE) install prefix=$(CURDIR)/debian/dovecot-common/usr sysconfdir=$(CURDIR)/debian/dovecot-common/etc/dovecot
+	$(MAKE) -C dovecot-sieve install DESTDIR=$(CURDIR)/debian/dovecot-common
+	mv $(CURDIR)/debian/dovecot-common/etc/dovecot/dovecot-example.conf $(CURDIR)/debian/dovecot-common/usr/share/dovecot/dovecot.conf
+	install -o root -g root -m 0644 $(CURDIR)/doc/dovecot-ldap-example.conf $(CURDIR)/debian/dovecot-common/usr/share/dovecot/dovecot-ldap.conf
+	install -o root -g root -m 0644 $(CURDIR)/doc/dovecot-sql-example.conf $(CURDIR)/debian/dovecot-common/usr/share/dovecot/dovecot-sql.conf
+	install -o root -g root -m 0644 $(CURDIR)/debian/conffile.md5sum/dovecot.conf.md5sum $(CURDIR)/debian/dovecot-common/usr/share/dovecot/
+	install -o root -g root -m 0644 $(CURDIR)/debian/conffile.md5sum/dovecot-ldap.conf.md5sum $(CURDIR)/debian/dovecot-common/usr/share/dovecot/
+	install -o root -g root -m 0644 $(CURDIR)/debian/conffile.md5sum/dovecot-sql.conf.md5sum $(CURDIR)/debian/dovecot-common/usr/share/dovecot/
+	install -D -m 0755 -o root -g root $(CURDIR)/debian/maildirmake.dovecot $(CURDIR)/debian/dovecot-common/usr/bin/maildirmake.dovecot
+	install -D -m 0755 -o root -g root $(CURDIR)/src/plugins/convert/convert-tool $(CURDIR)/debian/dovecot-common/usr/bin/convert-tool
+	install -o root -g root -m 0644 $(CURDIR)/debian/dovecot-common.overrides $(CURDIR)/debian/dovecot-common/usr/share/lintian/overrides/dovecot-common
+	mv $(CURDIR)/debian/dovecot-common/usr/share/doc/dovecot $(CURDIR)/debian/dovecot-common/usr/share/doc/dovecot-common
+	mv $(CURDIR)/debian/dovecot-common/usr/lib/dovecot/imap* $(CURDIR)/debian/dovecot-imapd/usr/lib/dovecot
+	mv $(CURDIR)/debian/dovecot-common/usr/lib/dovecot/pop3* $(CURDIR)/debian/dovecot-pop3d/usr/lib/dovecot
+	mv $(CURDIR)/debian/dovecot-common/usr/include/* $(CURDIR)/debian/dovecot-dev/usr/include
+	rmdir $(CURDIR)/debian/dovecot-common/usr/include
+	rm $(CURDIR)/debian/dovecot-common/etc/dovecot/dovecot-ldap-example.conf
+	rm $(CURDIR)/debian/dovecot-common/etc/dovecot/dovecot-sql-example.conf
+	chmod 0700 $(CURDIR)/debian/dovecot-common/var/run/dovecot
+	chmod 0750 $(CURDIR)/debian/dovecot-common/var/run/dovecot/login
+
+# Build architecture-independent files here.
+binary-indep: build install
+	# nothing to do here
+
+# Build architecture-dependent files here.
+binary-arch: build install
+	dh_testdir -a
+	dh_testroot -a
+	dh_installdocs -a
+	dh_installexamples -a
+	dh_installpam -a
+	mv $(CURDIR)/debian/dovecot-common/etc/pam.d/dovecot-common $(CURDIR)/debian/dovecot-common/etc/pam.d/dovecot
+	dh_installinit -pdovecot-common --init-script=dovecot -u"defaults 24"
+	dh_installman -a
+	dh_installman -p dovecot-common debian/maildirmake.dovecot.1 debian/dovecot.1
+	dh_installchangelogs -a ChangeLog
+	dh_link -a
+	dh_strip -a
+	dh_compress -a
+	dh_fixperms -a -Xetc/dovecot/dovecot.conf -Xetc/dovecot/dovecot-ldap.conf -Xetc/dovecot/dovecot-sql.conf -Xvar/run/dovecot -Xvar/run/dovecot/login
+	dh_installdeb -a
+	dh_shlibdeps -a
+	dh_gencontrol -a
+	dh_md5sums -a
+	dh_builddeb -a
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install 
+
+include /usr/share/dpatch/dpatch.make
--- dovecot-1.0.15.orig/debian/dovecot-dev.dirs
+++ dovecot-1.0.15/debian/dovecot-dev.dirs
@@ -0,0 +1 @@
+usr/include
--- dovecot-1.0.15.orig/debian/maildirmake.dovecot.1
+++ dovecot-1.0.15/debian/maildirmake.dovecot.1
@@ -0,0 +1,46 @@
+.\"                                      Hey, EMACS: -*- nroff -*-
+.\" First parameter, NAME, should be all caps
+.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
+.\" other parameters are allowed: see man(7), man(1)
+.TH "MAILDIRMAKE.DOVECOT" "1" "23 November 2005"
+.\" Please adjust this date whenever revising the manpage.
+.\"
+.\" Some roff macros, for reference:
+.\" .nh        disable hyphenation
+.\" .hy        enable hyphenation
+.\" .ad l      left justify
+.\" .ad b      justify to both left and right margins
+.\" .nf        disable filling
+.\" .fi        enable filling
+.\" .br        insert line break
+.\" .sp <n>    insert n+1 empty lines
+.\" for manpage-specific macros, see man(7)
+.SH NAME
+maildirmake.dovecot \- creates maildirs and maildir subfolders
+.SH SYNOPSIS
+.B maildirmake.dovecot
+\fBdirectory\fR [ \fRowner\fR ] \fR
+.br
+.SH DESCRIPTION
+.\" TeX users may be more comfortable with the \fB<whatever>\fP and
+.\" \fI<whatever>\fP escape sequences to invode bold face and italics, 
+.\" respectively.
+\fBmaildirmake.dovecot\fP creates a maildir and its maildir subfolders (cur, new, tmp).
+\fBdirectory\fP is the name of the new maildir. If \fBdirectory\fP exists cur, new, tmp directories are created inside it.
+.TP
+You can specify \fBowner\fP to change directories ownership if you have ownership modification permission.
+.TP
+\fBmaildirmake.dovecot\fP is very basic. It is strongly recommended to use \fBmaildirmake\fP if you want to do serious maildir management.
+.SH OPTIONS
+.TP
+\fB-h\fP
+Show options summary and exits immediately.
+.SH SEE ALSO
+.BR maildirmake (1).
+.BR maildir (5).
+.SH AUTHOR
+.TP
+dovecot was written by Timo Sirainen <tss@iki.fi>. maildirmake.dovecot script was written by Jaldhar H. Vyas
+.PP
+This manual page was written by Henry Precheur <henry@precheur.org>,
+for the Debian project (but may be used by others).
--- dovecot-1.0.15.orig/debian/compat
+++ dovecot-1.0.15/debian/compat
@@ -0,0 +1 @@
+5
--- dovecot-1.0.15.orig/debian/control
+++ dovecot-1.0.15/debian/control
@@ -0,0 +1,61 @@
+Source: dovecot
+Section: mail
+Priority: optional
+Maintainer: Dovecot Maintainers <jaldhar-dovecot@debian.org>
+Uploaders: Jaldhar H. Vyas <jaldhar@debian.org>, Fabio Tranchitella <kobold@debian.org>
+Build-Depends: debhelper (>= 5.0.0), automake1.9, autoconf, libtool, pkg-config, libssl-dev, libpam0g-dev, libldap2-dev, libpq-dev, libmysqlclient15-dev, libsqlite3-dev, libsasl2-dev | libsasl-dev, zlib1g-dev, libkrb5-dev, dpatch, bison, flex
+Build-Conflicts: linux-kernel-headers (<= 2.5.999-test7-bk-17), automake1.4
+Standards-Version: 3.7.3
+Vcs-Svn: svn://svn.debian.org/svn/collab-maint/deb-maint/dovecot/trunk
+Vcs-Browser: http://svn.debian.org/wsvn/collab-maint/deb-maint/dovecot/trunk/?op=log
+
+Package: dovecot-common
+Architecture: any
+Depends: ${shlibs:Depends}, libpam-runtime (>= 0.76-13.1), openssl, adduser, ucf (>= 2.0020)
+Replaces: dovecot
+Description: secure mail server that supports mbox and maildir mailboxes
+ Dovecot is a mail server whose major goals are security and extreme
+ reliability. It tries very hard to handle all error conditions and verify
+ that all data is valid, making it nearly impossible to crash. It should
+ also be pretty fast, extensible, and portable.
+ .
+ This package contains the files used by both the dovecot IMAP and POP3 
+ servers.
+
+Package: dovecot-dev
+Architecture: any
+Depends: ${shlibs:Depends}, dovecot-common (= ${binary:Version})
+Description: header files for the dovecot mail server
+ Dovecot is a mail server whose major goals are security and extreme
+ reliability. It tries very hard to handle all error conditions and verify
+ that all data is valid, making it nearly impossible to crash. It should
+ also be pretty fast, extensible, and portable.
+ .
+ This package contains header files needed to compile plugins for the
+ dovecot mail server.
+
+Package: dovecot-imapd
+Architecture: any
+Depends: ${shlibs:Depends}, dovecot-common (= ${binary:Version})
+Provides: imap-server
+Replaces: imap-server
+Description: secure IMAP server that supports mbox and maildir mailboxes
+ Dovecot is a mail server whose major goals are security and extreme
+ reliability. It tries very hard to handle all error conditions and verify
+ that all data is valid, making it nearly impossible to crash. It should
+ also be pretty fast, extensible, and portable.
+ .
+ This package contains the dovecot IMAP server.
+
+Package: dovecot-pop3d
+Architecture: any
+Depends: ${shlibs:Depends}, dovecot-common (= ${binary:Version})
+Provides: pop3-server
+Replaces: pop3-server
+Description: secure POP3 server that supports mbox and maildir mailboxes
+ Dovecot is a mail server whose major goals are security and extreme
+ reliability. It tries very hard to handle all error conditions and verify
+ that all data is valid, making it nearly impossible to crash. It should
+ also be pretty fast, extensible, and portable.
+ .
+ This package contains the dovecot POP3 server.
--- dovecot-1.0.15.orig/debian/dovecot-common.postinst
+++ dovecot-1.0.15/debian/dovecot-common.postinst
@@ -0,0 +1,72 @@
+#!/bin/sh
+set -e
+
+if [ "$1" = "configure" ]; then
+
+  for conffile in dovecot.conf dovecot-ldap.conf dovecot-sql.conf ; do
+    # Tell ucf that the file in /usr/share/dovecot is the latest
+    # maintainer version, and let it handle how to manage the real
+    # configuration file in /etc/dovecot.
+    ucf --three-way /usr/share/dovecot/$conffile /etc/dovecot/$conffile
+    ucfr dovecot-common /etc/dovecot/$conffile
+    if [ "$conffile" != "dovecot.conf" ] && [ -f "/etc/dovecot/$conffile" ]; then
+      chmod 0600 /etc/dovecot/$conffile
+    fi
+  done
+
+  if [ -n "`id -u imapd 2> /dev/null`" ]; then
+    /usr/sbin/deluser imapd || true
+    /usr/sbin/delgroup imapd || true
+  fi
+
+  if [ -z "`id -u dovecot 2> /dev/null`" ]; then
+    /usr/sbin/adduser --system --group --home /usr/lib/dovecot --gecos "Dovecot mail server" \
+                      --disabled-password --quiet dovecot || true
+  fi
+
+  # Fix permissions
+  chmod 755 /var/run/dovecot
+  chmod 750 /var/run/dovecot/login
+  chgrp dovecot /var/run/dovecot/login
+
+  ## SSL Certs
+  # Certs and key file
+  SSL_CERT="/etc/ssl/certs/dovecot.pem"
+  SSL_KEY="/etc/ssl/private/dovecot.pem"
+  
+  # Generate new certs if needed
+  if [ -e $SSL_CERT ] || [ -e $SSL_KEY ]; then
+    echo "You already have an SSL certificate or key for dovecot; not replacing"
+  else
+    echo "Creating generic self-signed certificate: $SSL_CERT"
+    echo "(replace with hand-crafted or authorized one if needed)."
+    cd /etc/ssl/certs
+    PATH=$PATH:/usr/bin/ssl
+    FQDN=`hostname -f`
+    MAILNAME=`cat /etc/mailname 2> /dev/null || hostname -f`
+    (openssl req -new -x509 -days 365 -nodes -out $SSL_CERT -keyout $SSL_KEY > /dev/null 2>&1 <<+
+.
+.
+.
+Dovecot mail server
+$FQDN
+$FQDN
+root@$MAILNAME
++
+    ) || echo "Warning : Bad SSL config, can't generate certificate."
+
+    if [ -e $SSL_CERT ] && [ -e $SSL_KEY ]; then
+      ucfr dovecot-common $SSL_CERT
+      ucfr dovecot-common $SSL_KEY
+    
+      chown root $SSL_CERT || true
+      chgrp dovecot $SSL_CERT || true
+      chmod 0644 $SSL_CERT || true
+      chown root $SSL_KEY || true
+      chgrp dovecot $SSL_KEY || true
+      chmod 0600 $SSL_KEY || true
+    fi
+  fi
+fi
+
+#DEBHELPER#
--- dovecot-1.0.15.orig/debian/dovecot-common.overrides
+++ dovecot-1.0.15/debian/dovecot-common.overrides
@@ -0,0 +1,5 @@
+dovecot-common: non-standard-file-perm etc/dovecot/dovecot.conf 0600 != 0644
+dovecot-common: non-standard-file-perm etc/dovecot/dovecot-ldap.conf 0600 != 0644
+dovecot-common: non-standard-file-perm etc/dovecot/dovecot-sql.conf 0600 != 0644
+dovecot-common: non-standard-dir-perm var/run/dovecot/ 0700 != 0755
+dovecot-common: non-standard-dir-perm var/run/dovecot/login/ 0750 != 0755
--- dovecot-1.0.15.orig/debian/dovecot-pop3d.dirs
+++ dovecot-1.0.15/debian/dovecot-pop3d.dirs
@@ -0,0 +1 @@
+usr/lib/dovecot
--- dovecot-1.0.15.orig/debian/dovecot-pop3d.prerm
+++ dovecot-1.0.15/debian/dovecot-pop3d.prerm
@@ -0,0 +1,12 @@
+#!/bin/sh
+set -e
+
+if [ -x "/etc/init.d/dovecot" ]; then
+	if [ -x /usr/sbin/invoke-rc.d ] ; then
+		invoke-rc.d dovecot stop
+	else
+		/etc/init.d/dovecot stop
+	fi
+fi
+
+#DEBHELPER#
--- dovecot-1.0.15.orig/debian/dovecot-pop3d.NEWS.Debian
+++ dovecot-1.0.15/debian/dovecot-pop3d.NEWS.Debian
@@ -0,0 +1,9 @@
+dovecot (0.99.13-1) unstable; urgency=low
+
+  * As of this version, dovecot-pop3d doesn't make changes to 
+    /etc/dovecot/dovecot.conf.  If this is your first time installing the
+    package, read /usr/share/doc/dovecot-pop3d/README.Debian to learn how
+    to enable the server.
+
+  -- Jaldhar H. Vyas <jaldhar@debian.org>  Fri, 31 Dec 2004 15:53:16 -0500
+
--- dovecot-1.0.15.orig/debian/conffile.md5sum/dovecot.conf.md5sum
+++ dovecot-1.0.15/debian/conffile.md5sum/dovecot.conf.md5sum
@@ -0,0 +1,4 @@
+76ce200ddd6962746ecdb36e72590e82  unstable_1.0.rc24-1
+9fca885eadd54b94201f876d3b1d0bcc  sarge_0.99.14-1sarge0
+1c5a8f791371eff93658754031d96d20  testing_1.0.rc15-2
+b6b57eb97e7de54bf8235b0b325f1dec  testing-security_1.0.beta2-1.1etch1
--- dovecot-1.0.15.orig/debian/conffile.md5sum/dovecot-sql.conf.md5sum
+++ dovecot-1.0.15/debian/conffile.md5sum/dovecot-sql.conf.md5sum
@@ -0,0 +1,3 @@
+4e0eae74d45add6c890207d35a64a633  unstable_1.0.rc24-1
+e0fac5b47f6bdaa057f8f3875d5dfd8b  testing_1.0.rc15-2
+f072557f02c0877a1821aec48357fcc2  testing-security_1.0.beta2-1.1etch1
--- dovecot-1.0.15.orig/debian/conffile.md5sum/dovecot-ldap.conf.md5sum
+++ dovecot-1.0.15/debian/conffile.md5sum/dovecot-ldap.conf.md5sum
@@ -0,0 +1,4 @@
+974e50066b04792901228a5fe8fada7e  unstable_1.0.rc24-1
+c56e1580610e921aea1da1f6c9ae49ed  sarge_0.99.14-1sarge0
+974e50066b04792901228a5fe8fada7e  testing_1.0.rc15-2
+f6efcb30fc2bf0c2f0bed4b6c24f7b3d  testing-security_1.0.beta2-1.1etch1
--- dovecot-1.0.15.orig/debian/dovecot.1
+++ dovecot-1.0.15/debian/dovecot.1
@@ -0,0 +1,61 @@
+.\"                                      Hey, EMACS: -*- nroff -*-
+.\" First parameter, NAME, should be all caps
+.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
+.\" other parameters are allowed: see man(7), man(1)
+.TH "DOVECOT" "1" "4 August 2007"
+.\" Please adjust this date whenever revising the manpage.
+.\"
+.\" Some roff macros, for reference:
+.\" .nh        disable hyphenation
+.\" .hy        enable hyphenation
+.\" .ad l      left justify
+.\" .ad b      justify to both left and right margins
+.\" .nf        disable filling
+.\" .fi        enable filling
+.\" .br        insert line break
+.\" .sp <n>    insert n+1 empty lines
+.\" for manpage-specific macros, see man(7)
+.SH NAME
+dovecot \- secure mail server that supports mbox and maildir mailboxes
+.SH SYNOPSIS
+\fBdovecot\fP [options]
+.br
+.SH DESCRIPTION
+.\" TeX users may be more comfortable with the \fB<whatever>\fP and
+.\" \fI<whatever>\fP escape sequences to invode bold face and italics, 
+.\" respectively.
+\fBdovecot\fP is a mail server whose major goals are security and extreme
+reliability. It tries very hard to handle all error conditions and verify that
+all data is valid, making it nearly impossible to crash. It should also be
+pretty fast, extensible, and portable.
+.SH OPTIONS
+.TP
+\fB-F\fP Start dovecot as foreground process (otherwise: start as daemon)
+.TP
+\fB-p\fP Ask for private SSL key password
+.TP
+\fB-a\fP Dump configuration and exit. This includes doing a syntax check.
+.TP
+\fB-n\fP Dump all non-default configuration items and exit
+.TP
+\fB-c <config-file>\fP
+Use <config-file> as path to the configuration instead of /usr/local/etc/dovecot.conf
+.TP
+\fB--version\fP Print version and exit
+.TP
+\fB--build-options\fP Print build options and exit
+.TP
+\fB--exec-mail <protocol> [<section>]\fP Start protocol from section
+.SH SIGNALS
+.TP
+Killing the Dovecot master process with a normal TERM signal does a clean shutdown.
+.TP
+To reload the configuration files, you can send a HUP signal to the dovecot process; an acknowledgement is written to log file.
+.TP
+If you specified log file paths manually in dovecot.conf instead of using syslog, you can send the USR1 signal to the dovecot process to make it close and reopen the log files.
+.SH AUTHOR
+.TP
+dovecot was written by Timo Sirainen <tss@iki.fi>.
+.PP
+This manual page was written by Fabio Tranchitella <kobold@debian.org>,
+for the Debian project (but may be used by others).
--- dovecot-1.0.15.orig/debian/dovecot-common.postrm
+++ dovecot-1.0.15/debian/dovecot-common.postrm
@@ -0,0 +1,40 @@
+#!/bin/sh
+set -e
+
+PATH=/usr/sbin:$PATH
+export PATH
+
+if [ "$1" = "purge" ] ; then
+	for conffile in /etc/dovecot/dovecot.conf /etc/dovecot/dovecot-ldap.conf /etc/dovecot/dovecot-sql.conf /etc/ssl/certs/dovecot.pem /etc/ssl/private/dovecot.pem; do
+		# we mimic dpkg as closely as possible, so we remove configuration
+		# files with dpkg backup extensions too:
+		### Some of the following is from Tore Anderson:
+		for ext in '~' '%' .bak .dpkg-tmp .dpkg-new .dpkg-old .dpkg-dist;  do
+			rm -f $conffile$ext
+		done
+		# remove the configuration file itself
+		rm -f $conffile
+		# and finally clear it out from the ucf database
+		if which ucf >/dev/null; then
+			ucf --purge $conffile
+		fi
+		if which ucfr >/dev/null; then
+			ucfr --purge dovecot-common $conffile
+		fi
+	done
+
+        userdel dovecot || true;
+	cd /etc/ssl/certs
+	PATH=$PATH:/usr/bin/ssl
+	if [ -f dovecot.pem ]; then
+		echo "SSL certificate /etc/ssl/certs/dovecot.pem is NOT removed."
+		echo "Please remove manually if required."
+	fi
+	if [ -f ../private/dovecot.pem ]; then
+		echo "SSL certificate /etc/ssl/private/dovecot.pem is NOT removed."
+		echo "Please remove manually if required."
+	fi
+	if [ -d /var/run/dovecot ]; then rm -rf /var/run/dovecot; fi
+fi
+
+#DEBHELPER#
--- dovecot-1.0.15.orig/debian/dovecot-pop3d.README.Debian
+++ dovecot-1.0.15/debian/dovecot-pop3d.README.Debian
@@ -0,0 +1,13 @@
+First time Installs
+-------------------
+
+dovecot-pop3d will not actually start up until the protocols= line in 
+/etc/dovecot/dovecot.conf contains one or both of the keywords pop3 (for
+POP3 on standard port 110) or pop3s (for POP3 over SSL on standard port 995.)
+
+After making the change, do:
+
+  # invoke-rc.d dovecot restart
+
+to enable pop3d.
+
--- dovecot-1.0.15.orig/debian/dovecot-common.pam
+++ dovecot-1.0.15/debian/dovecot-common.pam
@@ -0,0 +1,6 @@
+#%PAM-1.0
+
+@include common-auth
+@include common-account
+@include common-session
+
--- dovecot-1.0.15.orig/debian/dovecot-imapd.prerm
+++ dovecot-1.0.15/debian/dovecot-imapd.prerm
@@ -0,0 +1,12 @@
+#!/bin/sh
+set -e
+
+if [ -x "/etc/init.d/dovecot" ]; then
+	if [ -x /usr/sbin/invoke-rc.d ] ; then
+		invoke-rc.d dovecot stop
+	else
+		/etc/init.d/dovecot stop
+	fi
+fi
+
+#DEBHELPER#
--- dovecot-1.0.15.orig/debian/changelog
+++ dovecot-1.0.15/debian/changelog
@@ -0,0 +1,1168 @@
+dovecot (1:1.0.15-2.3+lenny1~bpo40+1) etch-backports; urgency=high
+
+  * Backport to etch. Disable building of drac module, not available in etch.
+  * This version includes fixes the the buffer overflow problems described in 
+    CVE-2009-3235.  Upgrading to this version is highly recommended.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Thu, 01 Oct 2009 13:18:05 -0400
+
+dovecot (1:1.0.15-2.3+lenny1) stable-security; urgency=high
+
+  * Non-maintainer upload by the Security Team.
+  * Fix for buffer overflow in SIEVE filtering allowing for privilege
+    escalation (closes: #546656). Thanks to Don Armstrong.
+
+ -- Giuseppe Iuculano <giuseppe@iuculano.it>  Wed, 23 Sep 2009 10:10:46 +0200
+
+dovecot (1:1.0.15-2.3) unstable; urgency=medium
+
+  * Non-maintainer upload
+  * Urgency medium due to RC bug fix
+  * Fix ManageSieve security hole "virtual users can edit scripts of other
+    virtual users" described at
+    <http://dovecot.org/list/dovecot/2008-November/035259.html>
+    (closes: #506031)
+
+ -- Dominic Hargreaves <dom@earth.li>  Wed, 19 Nov 2008 18:11:36 +0000
+
+dovecot (1:1.0.15-2.2) unstable; urgency=medium
+
+  * Non-maintainer upload
+  * Urgency medium due to RC bug fix
+  * Switch from byacc to bison to fix FTBFS (closes: #498679)
+  * Pull fix for CVE-2008-4577 from upstream VCS (partially fixes #502967)
+
+ -- Dominic Hargreaves <dom@earth.li>  Sat, 25 Oct 2008 19:32:34 +0100
+
+dovecot (1:1.0.15-2.1) unstable; urgency=medium
+
+  * Non-maintainer upload
+  * Urgency medium due to RC bug fix
+  * Don't regenerate the SSL certificate or key if either of them exist
+    (closes: #340008)
+
+ -- Dominic Hargreaves <dom@earth.li>  Wed, 22 Oct 2008 12:14:57 +0100
+
+dovecot (1:1.0.15-2) unstable; urgency=low
+
+  * debian/dovecot.1: added "This includes doing a syntax check" to the -a
+    option.
+  * debian/rules: now the package builds two times in a row without problems.
+    (Closes: #490201)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Sun, 27 Jul 2008 19:56:18 +0200
+
+dovecot (1:1.0.15-1) unstable; urgency=low
+
+  * New upstream release.
+  * debian/rules: clean the package before unpatching. (Closes: #490201)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Sun, 22 Jun 2008 09:06:01 +0200
+
+dovecot (1:1.0.14-1) unstable; urgency=low
+
+  * New upstream release.
+  * debian/patches/inbox_creation.dpatch: removed, merged upstream.
+  * debian/patches/allow_nets.dpatch: removed, merged upstream.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Tue, 03 Jun 2008 10:07:13 +0200
+
+dovecot (1:1.0.13-6) unstable; urgency=low
+
+  * debian/patches/inbox_creation.dpatch: added, use mail_privileged_group's
+    group when creating inboxes if the unprivileged user fails; upstream:
+    http://hg.dovecot.org/dovecot-1.0/rev/932768a879c6 (Closes: #471716)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Wed, 28 May 2008 12:58:17 +0200
+
+dovecot (1:1.0.13-5) unstable; urgency=low
+
+  * debian/patches/allow_nets.dpatch: added, allow_nets didn't work correctly
+    with big endian machines; patch from upstream, thanks Timo:
+    http://hg.dovecot.org/dovecot-1.0/rev/71c02fdf1b59
+
+ -- Fabio Tranchitella <kobold@debian.org>  Thu, 15 May 2008 14:48:14 +0200
+
+dovecot (1:1.0.13-4) unstable; urgency=low
+
+  * debian/patches/dovecot-MANAGESIEVE-9.3.dpatch: updated managesieve to
+    version 9.3.
+  * debian/dovecot-common.README.Debian: added a note about how to configure
+    dovecot to log to file instead of using syslog.
+  * debian/dovecot.1: added a SIGNALS section. (Closes: #479059)
+  * dovecot-sieve: updated to the last hg release (5c3ba11994cb).
+    (Closes: #479104)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Mon, 05 May 2008 17:28:21 +0200
+
+dovecot (1:1.0.13-3) unstable; urgency=low
+
+  * debian/rules: do not install anymore the ldap and sql example
+    configuration files under /etc. (Closes: #472674)
+  * debian/dovecot-common.postinst: really chmod
+    /etc/dovecot/dovecot-{ldap,sql}.conf files to 0600.
+  * debian/devecot-common.init: do not start the service if dovecot.conf
+    doesn't exist. (Closes: #475888)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Sun, 27 Apr 2008 22:42:37 +0200
+
+dovecot (1:1.0.13-2) unstable; urgency=low
+
+  * debian/rules: use aclocal-1.9 instead of aclocal. (Closes: #473754)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Tue, 01 Apr 2008 15:30:32 +0200
+
+dovecot (1:1.0.13-1) unstable; urgency=high
+
+  * New upstream release, fixes a security issue:
+    http://www.dovecot.org/list/dovecot-news/2008-March/000064.html
+
+ -- Fabio Tranchitella <kobold@debian.org>  Sun, 09 Mar 2008 19:22:20 +0100
+
+dovecot (1:1.0.12-1) unstable; urgency=high
+
+  * New upstream release. (Closes: #469457)
+  * debian/patches/dovecot-MANAGESIEVE-9.2.dpatch: updated, thanks to Marco
+    Nenciarini for the patch.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Thu, 06 Mar 2008 15:53:07 +0100
+
+dovecot (1:1.0.10-4) unstable; urgency=low
+
+  * debian/patches/autocreate.dpatch: added, thanks to Walter Reiner.
+  * debian/rules: use --with-ioloop=best instead of --with-ioloop=epoll, as
+    suggested by Timo. (Closes: #466296)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Mon, 18 Feb 2008 09:29:39 +0100
+
+dovecot (1:1.0.10-3) unstable; urgency=low
+
+  * debian/patches/dovecot-MANAGESIEVE-9.1.dpatch: added, thanks to Aleksey
+    Midenkov for providing a patch. (Closes: #416166)
+  * debian/dovecot-common.init: added $time to Should-Start. (Closes: #461543)
+  * debian/dovecot-common.postinst: do not add the dovecot user to the mail
+    group, it is not required by upstream. (Closes: #457123)
+  * debian/control: updated Standards-Version to 3.7.3, no changes required.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Sun, 10 Feb 2008 18:37:55 +0100
+
+dovecot (1:1.0.10-2) unstable; urgency=low
+
+  * debian/patches/mbox_snarf.dpatch: added, thanks to Bernd Kuhls.
+    (Closes: #462319)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Thu, 24 Jan 2008 10:12:02 +0100
+
+dovecot (1:1.0.10-1) unstable; urgency=high
+
+  * New upstream release, fixes a security bug.
+  * debian/patches/exec_check_for_none.dpatch: updated.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Sun, 30 Dec 2007 10:29:26 +0100
+
+dovecot (1:1.0.9-1) unstable; urgency=low
+
+  * New upstream release.
+  * debian/control: added the Vcs-Svn and Vcs-Browser fields.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Wed, 12 Dec 2007 08:10:11 +0100
+
+dovecot (1:1.0.8-2) unstable; urgency=low
+
+  * Provides a dovecot-dev package, thanks to Josh Triplett for providing a
+    patch. (Closes: #444812)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Tue, 04 Dec 2007 09:22:59 +0100
+
+dovecot (1:1.0.8-1) unstable; urgency=low
+
+  * New upstream release.
+  * debian/patches/unsupported-sasl-mech.dpatch: merged with upstream.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Thu, 29 Nov 2007 13:36:39 +0100
+
+dovecot (1:1.0.7-3) unstable; urgency=low
+
+  * debian/patches/dovecot-ssl.dpatch: provide mechanism to discover if ssl
+    client certificate is verified, patch from Stephen Gran. (Closes: #446555)
+  * debian/patches/pam-error-information.dpatch: fill auth information in pam
+    error, patch backported from upstream RCS. (Closes: #439246)
+  * debian/patches/unsupported-sasl-mech.dpatch: should use NO (not BAD) for
+    unsupported SASL mech, patch backported from upstream RCS. (Closes: #449324)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Wed, 14 Nov 2007 21:33:55 +0100
+
+dovecot (1:1.0.7-2) unstable; urgency=low
+
+  * debian/dovecot-common.postinst:
+    + don't fail if dovecot-ldap.conf or dovecot-sql.conf are removed; thanks
+      to Mathias Gug. (Closes: #448401)
+    + fix permissions of /var/run/dovecot and /var/run/dovecot/login.
+      (Closes: #446051)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Sun, 04 Nov 2007 10:06:06 +0100
+
+dovecot (1:1.0.7-1) unstable; urgency=low
+
+  * New upstream release.
+  * debian/rules: remove drac.so in the clean target. (Closes: #442548)
+  * debian/dovecot-common.init: implemented the reload action.
+    (Closes: #441032)
+  * Update protocoles option in configuration when installing/removing
+    -imapd/-pop3d packages. Thanks to Soren Hansen and Mathias Gug from
+    Ubuntu for providing a patch. (Closes: #447201)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Fri, 02 Nov 2007 23:06:17 +0100
+
+dovecot (1:1.0.5-1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Mon, 10 Sep 2007 09:25:59 +0200
+
+dovecot (1:1.0.3-3) unstable; urgency=low
+
+  * debian/dovecot-common.init: don't use the init script name to locate the
+    configuration file because it is not reliable. If you really want to start
+    multiple servers, just copy the init script and use the optional default
+    file to override the variables. (Closes: #437228)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Thu, 16 Aug 2007 09:17:01 +0200
+
+dovecot (1:1.0.3-2) unstable; urgency=low
+
+  * debian/rules: removed the --with-notify=inotify switch, it should be
+    detected automatically.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Thu, 09 Aug 2007 09:39:50 +0200
+
+dovecot (1:1.0.3-1) unstable; urgency=low
+
+  * New upstream release. (Closes: #434038, #432601)
+  * This release doesn't build dbox support out-of-the-box. (Closes: #431615)
+  * dovecot-sieve: updated to the last hg's tip. (Closes: #434079)
+  * debian/dovecot-*.README.Debian: don't call /etc/init.d scripts directly.
+    (Closes: #431991)
+  * debian/dovecot-common.init: updated with patches from Tom Metro, thanks!
+    (Closes: #426480, #422674)
+  * debian/dovecot-common.postinst: fixed a missing variable DOMAINNAME.
+    (Closes: #425917)
+  * debian/rules: moved the init script to the level 24, after the ntpdate
+    one. (Closes: #432723)
+  * debian/patches/00list: added dovecot-drac, again. (Closes: #353039)
+  * debian/rules: build with inotify and epoll support. (Closes: #419281)
+  * debian/dovecot.1: added a simple manpage for dovecot. (Closes: #426702)
+  * debian/copyright: added copyright exceptions as suggested by Timo.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Sat, 04 Aug 2007 20:11:36 +0200
+
+dovecot (1:1.0.0-1) unstable; urgency=low
+
+  * New upstream release, the final 1.0.0. Bumped epoch, because we used the
+    wrong version scheming in the past and I think it is worth to do so now
+    that 1.0.0 has been released.
+  * debian/watch: updated.
+  * Rebuilt with a new glibc in unstable, now we have inotify support.
+    (Closes: #395306)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Mon, 16 Apr 2007 08:42:32 +0200
+
+dovecot (1.0.rc31-1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Mon, 09 Apr 2007 11:55:45 +0200
+
+dovecot (1.0.rc30-2) unstable; urgency=low
+
+  * debian/dovecot-common.init: check if /etc/inetd.conf exists before
+    calling sed on it. (Closes: #417299)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Sun, 08 Apr 2007 16:58:30 +0200
+
+dovecot (1.0.rc30-1) unstable; urgency=low
+
+  * New upstream release.
+  * 
+
+ -- Fabio Tranchitella <kobold@debian.org>  Sat, 07 Apr 2007 11:17:45 +0200
+
+dovecot (1.0.rc29-1) unstable; urgency=low
+
+  * New upstream release.
+  * debian/rules: ship convert-tool in dovecot-common. (Closes: #416909)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Sat, 31 Mar 2007 14:15:39 +0200
+
+dovecot (1.0.rc28-1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Sun, 25 Mar 2007 14:02:28 +0200
+
+dovecot (1.0.rc27-1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Wed, 14 Mar 2007 14:39:04 +0100
+
+dovecot (1.0.rc26-4) unstable; urgency=low
+
+  * debian/dovecot-common.postinst: fixed a typo. (Closes: #414672)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Tue, 13 Mar 2007 08:45:47 +0100
+
+dovecot (1.0.rc26-3) unstable; urgency=low
+
+  * debian/control: depends on ucf (>= 2.0020). (Closes: #414032)
+  * debian/dovecot-common.postinst: handles better chmod/chown on package
+    upgrade. (Closes: #414257)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Mon, 12 Mar 2007 10:03:34 +0100
+
+dovecot (1.0.rc26-2) unstable; urgency=low
+
+  * debian/control:
+    + dovecot-{imapd,pop3d}: depends on the same-source dovecot-common.
+      (Closes: #414032)
+  * debian/rules: fixed permission for dovecot.conf. (Closes: #413995)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Fri,  9 Mar 2007 12:57:50 +0100
+
+dovecot (1.0.rc26-1) unstable; urgency=low
+
+  * New upstream release.
+  * Add support for ucf, thanks to Vincent Danjean for providing a full patch.
+    (Closes: #413081)
+  * debian/dovecot-common.init: create /var/run directories at start-up time.
+    (Closes: #376143)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Wed,  7 Mar 2007 11:26:56 +0100
+
+dovecot (1.0.rc25-1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Fri,  2 Mar 2007 13:44:44 +0100
+
+dovecot (1.0.rc24-1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Fri, 23 Feb 2007 15:47:30 +0100
+
+dovecot (1.0.rc23-1) unstable; urgency=low
+
+  * New upstream release.
+  * debian/patches/documentation.dpatch, debian/patches/xfs_quotas.dpatch:
+    removed, applied upstream.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Wed, 21 Feb 2007 09:34:44 +0100
+
+dovecot (1.0.rc22-2) UNRELEASED; urgency=low
+
+  * debian/watch: added.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Fri,  9 Feb 2007 12:15:34 +0100
+
+dovecot (1.0.rc22-1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Wed,  7 Feb 2007 09:38:34 +0100
+
+dovecot (1.0.rc21-1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Mon,  5 Feb 2007 16:23:33 +0100
+
+dovecot (1.0.rc19-1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Wed, 24 Jan 2007 00:03:38 +0100
+
+dovecot (1.0.rc18-2) unstable; urgency=low
+
+  * dovecot-sieve: updated to cvs version 1.0.1.
+  * debian/README.Debian: updated for the new setting mail_location, which
+    substitutes the old default_mail_env. (Closes: #408025)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Tue, 23 Jan 2007 10:15:36 +0100
+
+dovecot (1.0.rc18-1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Mon, 22 Jan 2007 18:15:02 +0100
+
+dovecot (1.0.rc17-1) unstable; urgency=low
+
+  * New upstream release.
+  * Updated dovecot-sieve from CVS.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Fri, 12 Jan 2007 09:42:47 +0100
+
+dovecot (1.0.rc15-2) unstable; urgency=medium
+
+  * debian/dovecot-common.README.Debian: updated details about raw logging;
+    thanks Chris Moore for providing a patch. (Closes: #400689)
+  * debian/patches/dovecot-example.dpatch: added a missing slash for an
+    absolute path. (Closes: #400830)
+  * debian/patches/dovecotpw.dpatch: applied patched to fix argument parsing
+    on some architectures. (Closes: #402075)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Mon, 18 Dec 2006 18:34:31 +0100
+
+dovecot (1.0.rc15-1) unstable; urgency=medium
+
+  * New upstream release.
+  * Fixes a security bug: Off-by-one buffer overflow with mmap_disable=yes.
+    (See: http://www.dovecot.org/list/dovecot-news/2006-November/000023.html)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Mon, 20 Nov 2006 12:47:39 +0100
+
+dovecot (1.0.rc14-1) unstable; urgency=medium
+
+  * New upstream release.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Thu, 16 Nov 2006 09:37:38 +0100
+
+dovecot (1.0.rc13-1) unstable; urgency=medium
+
+  * New upstream release.
+  * debian/rules:
+    + preserve upstream config.guess and config.sub. (Closes: #397404)
+    + really clean dovecot-sieve/src on clean target. (Closes: #397407)
+  * debian/control: added build-conflict with automake1.4. (Closes: #397409)
+  * dovecot-sieve/src/Makefile.am: move sieve plug-ins under
+    usr/lib/dovecot/lda/modules; thanks to Chris Vanden Berghe for pointing
+    this out.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Tue,  7 Nov 2006 09:26:56 +0100
+
+dovecot (1.0.rc12-1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Sun,  5 Nov 2006 16:52:52 +0100
+
+dovecot (1.0.rc10-3) unstable; urgency=medium
+
+  * debian/rules: fixed two typos in the configure call. (Closes: #395016)
+  * Included dovecot-sieve plug-in from CVS. (Closes: #394885)
+  * Urgency medium: we are near the freeze, and this release must be part
+    of etch.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Tue, 31 Oct 2006 06:30:45 +0000
+
+dovecot (1.0.rc10-2) unstable; urgency=low
+
+  * debian/patches/dovecot-example.dpatch: commented out a close brace.
+    (Closes: #394785)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Mon, 23 Oct 2006 08:17:13 +0000
+
+dovecot (1.0.rc10-1) unstable; urgency=low
+
+  * New upstream release. (Closes: #393004)
+  * debian/patches/dovecot-example.dpatch:
+    + added specific comments to the mail_extra_groups option.
+      (Closes: #383453)
+    + removed duplicated LDA section. (Closes: #391632)
+  * debian/dovecot.8: fixed a layout error. (Closes: #393080)
+  * debian/dovecot-common.init: added LSB headers.
+  * Switched to the upstream dovecot deliver (LDA).
+  * debian/patches/quota_v2.dpatch: added, thanks to Jonas Smedegaard.
+    (Closes: #377563)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Sun, 22 Oct 2006 08:55:16 +0000
+
+dovecot (1.0.rc7-1) unstable; urgency=low
+
+  * New upstream release. (Closes: #377840, #385101)
+  * debian/patches/dovecot-example.dpatch: set a default value for
+    pop3_uidl_format. (Closes: #383883)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Tue, 29 Aug 2006 10:38:17 +0200
+
+dovecot (1.0.rc6-1) unstable; urgency=low
+
+  * New upstream release:
+    + Fixed imap segfaults on small mbox files (2 bytes). (Closes: #377840)
+    + Fixed a known bug in dovecot's IDLE handler. (Closes: #351828)
+    + Added support for quota2. (Closes: #377563)
+  * debian/control: converted build-depends on linux-kernel-headers to
+    build-conflicts to help the GNU/kFreeBSD port. (Closes: #377479)
+  * debian/control: changed maintainer to "Dovecot Maintainers"; no changes
+    to the email addresses.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Tue, 15 Aug 2006 10:58:57 +0200
+
+dovecot (1.0.rc2-2) unstable; urgency=low
+
+  * patched the quota plugin to fix a missing symbol (Closes: #377018)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Fri,  7 Jul 2006 10:50:04 -0400
+
+dovecot (1.0.rc2-1) unstable; urgency=high
+
+  * New upstream release
+  * Update dovecot-lda to the latest version (05132006)
+  * IPv6 with SSL/TLS should work now.  (Closes: #374783)
+  * go back to using poll instead of epoll.  (Closes: #376222)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Thu,  6 Jul 2006 00:47:56 -0400
+
+dovecot (1.0.rc1-1) unstable; urgency=low
+
+  * New upstream release.
+  * Add a build-dependency on linux-kernel-headers for the xfs quotas 
+    stuff.  Make it higher than the version in sarge because sarges xfs
+    includes are too old.  If any knowledgeable person would like to 
+    give me a patch for this, please do.  (Closes: #374793)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Wed, 28 Jun 2006 11:42:07 -0400
+
+dovecot (1.0.beta9-1) unstable; urgency=low
+
+  * New upstream release
+  * Added XFS quota support.  Thanks  Pawel Jarosz <pj@rsi.pl> 
+    (Closes: #373936)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Mon, 19 Jun 2006 16:55:20 -0400
+
+dovecot (1.0.beta8-4) unstable; urgency=high
+
+  * Unfortunately, the patch in the last version broke the mysql module.
+    Fixed thanks to Martin Pitt.  (Closes: #369359, #373227)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Sun, 11 Jun 2006 16:27:43 -0400
+
+dovecot (1.0.beta8-3) unstable; urgency=high
+
+  * [SECURITY] SQL injection could occur in the postgresql module with
+    certain client character encodings. (See CVE-2006-2314)
+    Used the patch from upstream and Martin Pitt <martin.pitt@ubuntu.com>.
+    Thanks Martin.  (Closes: #369359)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Sun, 11 Jun 2006 15:33:55 -0400
+
+dovecot (1.0.beta8-2) unstable; urgency=high
+
+  * Don't chown/chmod ssl certificate unless we created it.
+    (Closes: #364766)
+  * Upstream fixed the crash if passwd-file had entries without passwords.
+    (Closes: #361536)
+  * fixed up the last versions changelog to better describe the security
+    problem which was fixed there.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Sun, 21 May 2006 13:16:17 -0400
+
+dovecot (1.0.beta8-1) unstable; urgency=high
+
+  * New upstream release.
+  * [SECURITY] Fixes a directory traversal vulnerability.
+    (see: http://www.dovecot.org/list/dovecot-news/2006-May/000006.html 
+    and CVE-2006-2414)
+  * Set urgency to high: this version fixes a security bug
+  * Standards-Version: 3.7.2, no changes needed.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Sat, 13 May 2006 22:46:16 +0200
+
+dovecot (1.0.beta7-1) unstable; urgency=low
+
+  * New upstream version.
+  * Added sqlite support.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Wed, 12 Apr 2006 23:25:41 -0400
+
+dovecot (1.0.beta5-1) unstable; urgency=low
+
+  * New upstream version.  Also updated dovecot-lda from CVS.
+  * debian/control.  Added build-depends on flex to prevent FTBS.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Thu,  6 Apr 2006 16:22:46 -0400
+
+dovecot (1.0.beta3-3) unstable; urgency=low
+
+  * Compile against the newer mysql library. (Closes: #356729)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Wed, 22 Mar 2006 13:43:04 +0000
+
+dovecot (1.0.beta3-2) unstable; urgency=low
+
+  [Fabio Tranchitella]
+  * debian/control: added build-depends on byacc.
+  * debian/rules: removed --with-vpopmail option, because libvpopmail-dev
+    is in contrib and we don't wanto to have dovecot build-depends on it.
+  * debian/patches/dovecot-example.dpatch: added two small commented 
+    block of configuration for dovecot-lda.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Sun, 26 Feb 2006 20:59:06 +0000
+
+dovecot (1.0.beta3-1) unstable; urgency=high
+
+  [Fabio Tranchitella]
+  * New upstream release, which fixes two security related bugs.
+    CVE-2006-0730 (Closes: #353341)
+  * Included dovecot-lda (ver. 20060209). (Closes: #353307, #347348, #333962)
+
+  [ Jaldhar H. Vyas ]
+  * Removed the code for upgrading impad.pem.  This might bite if you if
+    you try and upgrade a woody version of dovecot to this one.  So
+    don't do that.  (Closes: #337715)
+  * dovecot-imapd,dovecot-pop3d: depend on dovecot-common >= 1.0beta3-1 
+    as the way SSL parameters are generated has changed.  (Closes: #353404)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Fri, 17 Feb 2006 22:06:21 -0500
+
+dovecot (1.0.beta2-1) unstable; urgency=low
+
+  [Fabio Tranchitella]
+  * New upstream release.
+  * debian/rules: compile with vpopmail support. (Closes: #347838)
+  * debian/patches: removed zlib patch (merged with upstream).
+
+ -- Fabio Tranchitella <kobold@debian.org>  Thu,  2 Feb 2006 21:38:32 +0000
+
+dovecot (1.0.alpha5-1) unstable; urgency=low
+
+  [Fabio Tranchitella]
+  * New upstream release.
+  * Compile dovecot with Kerberos support. (Closes: #338384)
+  * Fixed a small typo in mbox specification. (Closes: #339789)
+  * Added man page for maildirmake.dovecot, thanks to Henry Precheur.
+    (Closes: #340498)
+  * Use /usr/lib/dovecot/modules as basedir for dynamic modules.
+    Upstream suggests /usr/lib/dovecot, but we already use it as 
+    libexec directory.
+
+ -- Fabio Tranchitella <kobold@debian.org>  Wed, 21 Dec 2005 13:44:38 +0000
+
+dovecot (1.0.alpha4-1) unstable; urgency=low
+
+  [Jaldhar H. Vyas]
+  * New upstream version.
+  * Made sure the default dovecot.conf includes mail_extra_groups=mail
+    (Closes: #336476) somehow this edit got lost at some point.
+  * use ISO8601 date format as default value for log_timestamp in
+    /etc/dovecot/dovecot.conf  (Closes: #333059)
+  * stop shipping {arch} directories in source (Closes: #334646)
+  * Include plugin for compressed mboxen (Closes: #332384)
+  * updated NEWS.Debian to warn users that the dovecot.conf syntax has
+    changed (Closes: #334209)
+  * Remember, remember, the 5th of November.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Sat,  5 Nov 2005 23:19:19 -0500
+
+dovecot (1.0.alpha3-2) unstable; urgency=low
+
+  [Jaldhar H. Vyas]
+  * dovecot-common: When creating the dovecot user in the postinst,
+    the --ingroup option to adduser to add dovecot to group
+    mail isn't used anymore.
+    (Closes: #330960, #331106)
+  * commented out userdb passdb from default configuration.  Most 
+    people won't need that. (Closes: #330978)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Thu,  6 Oct 2005 14:25:33 -0400
+
+dovecot (1.0.alpha3-1) unstable; urgency=low
+
+  [ Fabio Tranchitella ]
+  * New upstream release (dovecot-1.0.alpha.3)
+  * debian/patches/ipv6_v6only.dpatch: removed, upstream accepted it.
+  * debian/dovecot-common.postinst: removed bashisms.
+  * debian/dovecot-common.postinst: add dovecot user to group mail.
+    (Closes: #323921)
+  * debian/control: removed conflicts with imap-server and pop3-server,
+    added replaces instead. (Closes: #324480)
+  [ Jaldhar H. Vyas ]
+  * No longer crashes when using LDAP as userdb/passdb (Closes: #320388)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Mon, 26 Sep 2005 01:42:09 -0400
+
+dovecot (0.99.20050712-2) unstable; urgency=low
+
+  * Fabio Tranchitella <kobold@debian.org>
+    + debian/control: dovecot-common has to depend on adduser.
+    + debian/patches/documentation.dpatch: some cosmetic fixes about mysql
+      backend.
+  * Jaldhar H. Vyas <jaldhar@debian.org>
+    + debian/control: tighten dovecot-imapd and dovecot-pop3d's dependency on 
+      dovecot-common (Closes: #319465)
+    + debian/patches/dovecot-example.dpatch: some more fixes to default
+      configuration.  (Closes: #319413, #319941)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Fri, 29 Jul 2005 15:37:52 -0400
+
+dovecot (0.99.20050712-1) unstable; urgency=low
+
+  * Fabio Tranchitella <kobold@debian.org>
+    + New upstream version (dovecot-stable, last update 20050712).
+      (Closes: #312893)
+    + debian/control: Standards-Version: 3.6.2 (no changes needed).
+    + debian/patches/dovecot-sql.dpatch: use the right path for mysql socket.
+      (Closes: #298874)
+  * Jaldhar H. Vyas <jaldhar@debian.org>
+    + Removed dovecot package as it was just a woody->sarge transitional
+      pseudo-package.
+    + Apply patch to debian/dovecot-common.init to help when manually
+      starting dovecot.  Thanks Roland Stigge.  (Closes: #309679)
+    + Apply patch to src/lib/network.c to support IPV6_V6ONLY.  Thanks
+      Marco D'Itri. (Closes: #308652)
+    + depend on the latest postgresql library.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Wed, 20 Jul 2005 06:30:37 -0400
+
+dovecot (0.99.14-1) unstable; urgency=low
+
+  * New upstream version.
+  * dovecot-common: another postinst regexp fix for SSL cert/key files.
+    (Closes: #294989)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Sat, 12 Feb 2005 21:34:33 -0500
+
+dovecot (0.99.13-6) unstable; urgency=high
+
+  * dovecot-common: *sigh* another init script fix.  Hopefully we now
+    fully deal with dovecot being run from inetd.  Thanks again to Magnus
+    Holmgren.  (Closes: #293348)
+  * High again so -5 doesn't get into sarge.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Mon,  7 Feb 2005 02:58:30 -0500
+
+dovecot (0.99.13-5) unstable; urgency=high
+
+  * dovecot-common: typo in postinst resulted in incorrect generation of
+    keys for first-time installers.  Hence urgency high.
+  * dovecot-common: In init script, make extra check to make sure an 
+    IMAP or POP3 server called from inetd is dovecot and not some other 
+    random inferior product.  (Closes: #293348)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Sat,  5 Feb 2005 13:56:31 -0500
+
+dovecot (0.99.13-4) unstable; urgency=low
+
+  * build depend on libmysqlclient12
+  * dovecot-common: Allow STARTTLS to work when dovecot is run from inetd
+    Thanks Magnus Holmgren (Closes: #290985)
+  * dovecot-common: let init script exit if dovecot is being run from inetd
+    Thanks Magnus Holmgren (Closes: #292195)
+  * dovecot-common: fix a number of problems in postinst
+    + fails if /etc/ssl/certs or /etc/ssl/private doesn't exist
+    + certs cannot be generated and upgrade fails if openssl is not
+      configured.  Fail more gracefully if this is the case.
+    + read the name and path for the cert from dovecot.conf instead of
+      hardcoding it.
+      Thanks Frederic Pauget (Closes: #292344)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Sun, 30 Jan 2005 15:20:03 -0500
+
+dovecot (0.99.13-3) unstable; urgency=high
+
+  * Oops -2 had to be urgency=high so -1 doesn't get into sarge.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Sat,  8 Jan 2005 12:11:38 -0500
+
+dovecot (0.99.13-2) unstable; urgency=low
+
+  * dovecot-imapd, dovecot-pop3d:  It occurred to me that the effects of 
+    fixing #288391 will cause confusion in the minds of new installers so I 
+    should add a warning in README.Debian and NEWS.Debian in a vain 
+    effort to stave off swarms of bug reports.  (Vain, because no one 
+    actually reads documentation anyway.)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Sat,  8 Jan 2005 11:29:59 -0500
+
+dovecot (0.99.13-1) unstable; urgency=high
+
+  * New upstream version.
+  * dovecot-imapd, dovecot-pop3d: No longer mess with dovecot.conf in postinst
+    (Closes: #288391)
+  * urgency high due to #288391 being a release-critical bug.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Fri,  7 Jan 2005 17:37:08 -0500
+
+dovecot (0.99.12-1) unstable; urgency=low
+
+  * New upstream version.  (Yes I know 0.99.13 is just around the corner.)
+  * SASL is reenabled so this bug ("Dovecot seems not to require SASL")
+    is no longer valid (Closes: #272093)
+  * Configuration files moved to /etc/dovecot (Closes: #276183)
+  * Permissions on /var/run/dovecot and /var/run/dovecot/login no longer
+    give warnings. (Closes: #283996)
+  * SSL certificate is world readable (Closes: #277114).
+  * Thanks to Jan Buren, much extra documentation has been added to
+    /usr/share/doc/dovecot-common/README.Debian
+  * Lintian overrides added.
+  * Happy new year to all you Gregorians
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Fri, 31 Dec 2004 15:55:07 -0500
+
+dovecot (0.99.11-3) unstable; urgency=medium
+
+  * applied dovecot-large-header-fix patch to prevent 100% CPU 
+    utilization when dealing with really large headers.
+    (Closes: #271458)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Mon, 13 Sep 2004 20:20:23 -0400
+
+dovecot (0.99.11-2) unstable; urgency=low
+
+  * Eliminated duplicate stanza in dovecot.conf (Closes: #270181)
+  * Reapplied CRAM-MD5 patch.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Mon,  6 Sep 2004 01:24:53 -0400
+
+dovecot (0.99.11-1) unstable; urgency=low
+
+  * New upstream release.
+  * patch to give bug reporting address in configure.ac.
+    Thanks Matthias Andree.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Sat,  4 Sep 2004 14:24:57 -0400
+
+dovecot (0.99.10.9-2) unstable; urgency=low
+
+  * screw mipsel.
+  * Added PAM_RHOST patch.  Thanks Dean Gaudet.  (Closes: #264712)
+  * Added CRAM-MD5 patch.  Thanks Joshua Goodall.
+  * Added unexpected EOF patch from Timo.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Tue, 17 Aug 2004 01:13:20 -0400
+
+dovecot (0.99.10.9-1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Mon,  2 Aug 2004 18:56:02 -0400
+
+dovecot (0.99.10.8-1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Fri, 30 Jul 2004 08:17:51 -0400
+
+dovecot (0.99.10.7-1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Wed, 14 Jul 2004 07:29:49 -0400
+
+dovecot (0.99.10.6-3) unstable; urgency=low
+
+  * Patched so dovecot follows symlinks to directories again.
+    (Closes: #256061)
+  * Changed the priority of init script so it is run after postgresql
+    (Closes: #256068)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Thu, 24 Jun 2004 23:57:50 -0400
+
+dovecot (0.99.10.6-2) unstable; urgency=high
+
+  * I needed to enable one more parameter in the configuration in order 
+    to get dot-locking working.  Hence this should still be high urgency.
+    (Really Closes: #185335)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Mon, 21 Jun 2004 20:02:29 -0400
+
+dovecot (0.99.10.6-1) unstable; urgency=high
+
+  * New upstream version.
+    + finally fixes dot-locking so I think it deserves high priority for
+      sarge.  (Closes: #185335)
+  * dovecot: fixed a typo in description (Closes: #254415)
+  * dovecot-common: man page for dovecot added.  Thanks Kai Hendry.
+    (Closes: #253482)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Sat, 19 Jun 2004 23:18:39 -0400
+
+dovecot (0.99.10.5-4) unstable; urgency=high
+
+  * Crap, typo in dovecot-common.postinst sorry.  This should only 
+    affect new installs though.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Fri, 11 Jun 2004 22:30:41 -0400
+
+dovecot (0.99.10.5-3) unstable; urgency=high
+
+  * SECURITY:  set permissions on config files to 0600 to prevent
+    disclosure of sensitive information to local users. (Closes: #253760)
+  * SECURITY: Tightened permissions on generated SSL certificate.
+    (Closes: #253833)
+  * Made a note that dovecot-openssl.conf is not needed on Debian
+    because we generate a certificate in dovecot-commons' postinst.
+    (Closes: #253774)
+  * Added maildir-autocreate patch.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Fri, 11 Jun 2004 09:12:07 -0400
+
+dovecot (0.99.10.5-2) unstable; urgency=low
+
+  * Added maildir-stat patch.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Thu, 10 Jun 2004 14:39:28 -0400
+
+dovecot (0.99.10.5-1) unstable; urgency=low
+
+  * New upstream version.
+  * Enabled mysql support.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Thu, 27 May 2004 13:21:42 -0400
+
+dovecot (0.99.10.4-5) unstable; urgency=high
+
+  * Switched to using openssl as dovecot segfaults with gnutls7 and is
+    not compatible with gnutls10 (Closes: #244570)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Mon, 19 Apr 2004 22:45:21 -0400
+
+dovecot (0.99.10.4-4) unstable; urgency=low
+
+  * Added a patch to src/auth/db-pgsql.c from Zsolt VARGA.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Tue, 30 Mar 2004 12:49:31 -0500
+
+dovecot (0.99.10.4-3) unstable; urgency=high
+
+  * dovecot-common: Fix postinst to no longer delete /etc/pam.d/imap 
+    (Closes: #232832)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Mon, 15 Mar 2004 10:27:52 -0500
+
+dovecot (0.99.10.4-2) unstable; urgency=low
+
+  * dovecot-common: now replaces: dovecot for smoother upgrades.
+    (Closes: #223666)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Thu, 11 Dec 2003 09:18:12 -0500
+
+dovecot (0.99.10.4-1) unstable; urgency=low
+
+  * New upstream version
+    + This fixes the curruption of .subscriptions files wth folders in
+      maildir format. (Closes: #222272)
+  * Some extra information included in dovecot-common.README.Debian.
+    (Closes: #221106)  There should probaly be more so if you have ideas 
+    let me know.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Mon,  1 Dec 2003 23:41:00 -0500
+
+dovecot (0.99.10.2-1) unstable; urgency=low
+
+  * New upstream version.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Tue, 11 Nov 2003 17:21:45 -0500
+
+dovecot (0.99.10-11) unstable; urgency=low
+
+  * Build depend on gnutls 7 instead of 5 (Closes: #219523)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Thu,  6 Nov 2003 22:25:00 -0500
+
+dovecot (0.99.10-10) unstable; urgency=low
+
+  * maildirmake.dovecot will now let you create maildirs whose names 
+   have spaces in them and chown them to a specified user.  Thanks Paul
+   Slootman (Closes: #219168)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Tue,  4 Nov 2003 20:00:25 +0000
+
+dovecot (0.99.10-9) unstable; urgency=low
+
+  * Don't use SASL2 as upstream says the support is broken.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Tue, 23 Sep 2003 16:27:11 +0000
+
+dovecot (0.99.10-8) unstable; urgency=low
+
+  * Patched so suid works on 2.6 kernels.  Thanks Peter Gervai.
+    (Closes: #211420)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Wed, 17 Sep 2003 17:59:10 +0000
+
+dovecot (0.99.10-7) unstable; urgency=low
+
+  * Yet another init script fix.  It should be ok now.  Thanks once again
+    to Alexis Iglauer.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Wed, 27 Aug 2003 10:56:04 -0400
+
+dovecot (0.99.10-6) unstable; urgency=low
+
+  * fix some errors in init script (closes: #207464)
+    Thanks to Adam Lackorzynski and Alexis Iglauer.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Wed, 27 Aug 2003 09:02:23 -0400
+
+dovecot (0.99.10-5) unstable; urgency=high
+
+  * dovecot-pop3d, dovecot-imapd: make sure init script doesn't 
+    attempt to start the daemons if unconfigured thus preventing 
+    segfault on startup.  (Closes: #206992, #207140)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Mon, 25 Aug 2003 16:40:53 -0400
+
+dovecot (0.99.10-4) unstable; urgency=low
+
+  * Updated PAM configuration to the new scheme and added appropriate 
+    dependency.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Fri, 22 Aug 2003 12:54:54 -0400
+
+dovecot (0.99.10-3) unstable; urgency=low
+
+  * dovecot-pop3d: Patch for proper PAM service name.
+  * dovecot-imapd, dovecot-pop3d: Make sure there is an appropriate 
+    entry in the protocol = line in /etc/dovecot.conf so the service 
+    will start up without errors. (Closes: #204213)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Thu, 21 Aug 2003 13:47:00 -0400
+
+dovecot (0.99.10-2.1) unstable; urgency=low
+
+  * Non-maintainer upload at request of maintainer.
+  * Fix segfault on alpha caused by time_t size. Closes: #203892.
+  * Fix segfault when user's home directory is left empty.
+
+ -- Scott James Remnant <scott@netsplit.com>  Wed,  6 Aug 2003 01:47:16 +0100
+
+dovecot (0.99.10-2) unstable; urgency=low
+
+  * corrected paths to example and configuration files in sample config
+    (Closes: #199740)
+  * Added postgresql support.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Thu,  3 Jul 2003 16:45:49 -0400
+
+dovecot (0.99.10-1) unstable; urgency=low
+
+  * New upstream release.
+  * PAM service name has changed to dovecot (for IMAP and POP3.)  I've included 
+    code to move /etc/pam.d/imap to /etc/pam.d/dovecot but if things suddenly 
+    stop working, this is the first thing to check.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Thu, 26 Jun 2003 22:31:07 -0400
+
+dovecot (0.99.10-0.rc2) unstable; urgency=low
+
+  * New upstream release.  Fixes broken imaps support.
+  * Typo in configure options that broke LDAP support on woody corrected.
+  * Only start /usr/sbin/dovecot if either the IMAP or POP3 servers are 
+    installed. (Closes: #192066)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Mon, 23 Jun 2003 23:26:04 -0400
+
+dovecot (0.99.9.1-1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Sun,  4 May 2003 21:49:55 -0400
+
+dovecot (0.99.9-1) unstable; urgency=low
+
+  * New upstream release.
+  * The IMAP and POP3 servers have been split into seperate package so you
+    don't have to install both.  There is also a dovecot-common package
+    for the parts they share.  The dovecot package is now a dummy just for 
+    transitioning to this new scheme.  (Closes: #187826)
+  * Allow chmod in maildirmake.dovecot to fail gracefully (Closes: #191244)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Wed, 30 Apr 2003 08:53:46 -0400
+
+dovecot (0.99.8.1-4) unstable; urgency=low
+
+  * Added a build-depends on libsasl-dev (Closes: #187516)
+  * Enabled pop3 service.  However it is still turned off in the config file by
+    default so as to not surprise anyone who thought they only had an IMAP
+    server.
+  * Consequently, changed "IMAP server" in descriptions etc. to "mail server".
+  * only skip key generation if both /etc/ssl/certs/dovecot.pem and
+    /etc/ssl/private/dovecot.pem exist.  (Closes: #187638)
+  * post-0.99.8.1 patch: Fix renaming subfolders with maildir.
+  * post-0.99.8.1 patch: Fix other maildir subfolder problems.
+  * post-0.99.8.1 patch: Fix partial body fetches.
+  * post-0.99.8.1 patch: Fix using LITERAL+APPEND.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Sat,  5 Apr 2003 14:13:52 -0500
+
+dovecot (0.99.8.1-3) unstable; urgency=low
+
+  * Fixed bashism and perlism(!) in maildirmake.dovecot.  Thanks Clint
+    Adams. (Closes: #185768)
+  * Enabled LDAP support.  The configuration is in /etc/dovecot-ldap.conf
+    but is commented out.
+  * Happy new year to all Debian users still on the Julian calendar.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Tue, 25 Mar 2003 09:25:37 -0500
+
+dovecot (0.99.8.1-2) unstable; urgency=low
+
+  * Make a seperate check for /etc/ssl/private/imapd.pem in case we don't
+    have it. (Closes: #185334)
+  * Move check for dovecot user and creation code to postinsts' configure 
+    phase. (Closes: #185333)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Wed, 19 Mar 2003 01:50:10 -0500
+
+dovecot (0.99.8.1-1) unstable; urgency=low
+
+  * new upstream release.  (Closes: #184131)  Does not include the POP3
+    server or LDAP and SASL support for now so we can get into the archive
+    quickly.  Fixes segfaults in imap-master (Closes: #184231)  Fixes
+    faulty mail autodetection presets (Closes: #179625)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Sun, 16 Mar 2003 15:47:54 -0500
+
+dovecot (0.99.7-4) unstable; urgency=low
+
+  * Fixed location of handlers. (Closes: #179273)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Fri, 31 Jan 2003 18:37:23 -0500
+
+dovecot (0.99.7-3) unstable; urgency=low
+
+  * doh pam file should be called imap not dovecot (Closes: #179180)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Fri, 31 Jan 2003 14:12:29 -0500
+
+dovecot (0.99.7-2) unstable; urgency=low
+
+  * Added two upstream patches to fix broken plain authentication and 
+    building with vpopmail support (which is not enabled in the Debian 
+    package yet.)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Fri, 17 Jan 2003 14:24:22 -0500
+
+dovecot (0.99.7-1) unstable; urgency=low
+
+  * New upstream release.
+  * Syslog no longer fills up with entries when dovecot is misconfigured.
+    (Closes: #175507)
+  * startup is logged now. shutdowns were already logged. (Closes: #175509)
+  * dovecot will not remove your SSL certificates now.  (Closes: #175282)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Wed, 15 Jan 2003 00:58:23 -0500
+
+dovecot (0.99.6rc2-1) unstable; urgency=low
+
+  * New upstream release.
+  * Includes patch which stops dovecot from dying on alpha (Closes: #175577)
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Wed,  8 Jan 2003 14:22:09 -0500
+
+dovecot (0.99.5-1) unstable; urgency=low
+
+  * New upstream release with many fixes and improvements.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Fri,  3 Jan 2003 00:53:26 -0500
+
+dovecot (0.99.4-1) unstable; urgency=low
+
+  * Initial Release.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Wed, 18 Dec 2002 09:44:39 -0500
+
--- dovecot-1.0.15.orig/debian/watch
+++ dovecot-1.0.15/debian/watch
@@ -0,0 +1,4 @@
+# watch control file for uscan
+version=2
+# Site                                      Version Script
+http://dovecot.org/releases/1.0/dovecot-(.*\.[0-9]+).tar.gz debian uupdate
--- dovecot-1.0.15.orig/debian/dovecot-common.NEWS.Debian
+++ dovecot-1.0.15/debian/dovecot-common.NEWS.Debian
@@ -0,0 +1,44 @@
+dovecot (1:1.0.12-1) unstable; urgency=low
+
+  * mail_extra_groups=mail setting is often used insecurely to give Dovecot
+    access to create dotlocks to /var/mail directory. The setting has been
+    replaced by mail_privileged_group and mail_access_groups.
+    Read also: http://dovecot.org/list/dovecot/2008-March/029196.html
+
+ -- Fabio Tranchitella <kobold@debian.org>  Thu, 06 Mar 2008 15:46:03 +0100
+
+dovecot (1.0.beta3-1) unstable; urgency=low
+
+  * Starting from this release, dovecot-lda is included in dovecot-common.
+    The previous executable deliver has been removed from the upstream 
+    source package.
+  * Other new features recently added include
+    + vpopmail support
+    + quota support
+    + GSSAPI support
+  * All these new features mean there are some configuration file changes 
+    please review the default /etc/dovecot/dovecot.conf and merge in any
+    new bits.  (If you don't use any new features, your configuration should
+    remain compatible.)
+
+ -- Fabio Tranchitella <kobold@debian.org>  Fri, 17 Feb 2006 17:05:37 +0000
+
+dovecot (1.0.alpha4-1) unstable; urgency=low
+
+  * This is the 1.0alpha branch.  Once again there have been incompatible 
+    changes to the syntax of /etc/dovecot/dovecot.conf. 
+  * The dovecot-common package now includes the zlib plugin for compressed
+    mboxen.  Make sure you have mail_use_modules=yes in imap and/or pop3 
+    section in /etc/dovecot/dovecot.conf if you want to use this.
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Wed, 20 Jul 2005 06:30:37 -0400
+
+dovecot (0.99.20050712-1) unstable; urgency=low
+
+  * This is the 1.0stable development branch.  There have been major 
+    changes and new features have been added so check your configuration 
+    carefully.  In particular, /etc/dovecot/dovecot-mysql.conf and 
+    /etc/dovecot/dovecot-pgsql.conf have been replaced by
+    /etc/dovecot/dovecot-sql.conf .
+
+ -- Jaldhar H. Vyas <jaldhar@debian.org>  Wed, 20 Jul 2005 06:30:37 -0400
--- dovecot-1.0.15.orig/debian/dovecot.8
+++ dovecot-1.0.15/debian/dovecot.8
@@ -0,0 +1,101 @@
+.TH dovecot 8 "June 2004"
+.\" Copyright (c) 2004 Kai Hendry (hendry@iki.fi)
+.SH NAME
+dovecot \- An IMAP and POP3 mail server
+.SH SYNOPSIS
+.PP
+.BI "dovecot [options]
+.SH DESCRIPTION
+.B dovecot
+is a POP3 and IMAP mail server that can work with standard mbox and maildir 
+formats.  It is fully compatible with UW-IMAP and Courier IMAP servers as 
+well as mail clients accessing the mailboxes directly.
+.PP
+.B dovecot
+can be setup to authenticate against LDAP, Postgres or MYSQL databases, but by 
+default authentication is with 
+.BR PAM(7)
+.
+.PP
+.B dovecot
+is normally started during the system boot, and runs as a background process
+but it can also be run through
+.BR inetd(8)
+.
+In the latter case, the
+.B /usr/lib/dovecot/imap-login
+or
+.B /usr/lib/dovecot/pop3-login
+binaries are run directly not
+.B /usr/sbin/dovecot
+.
+.PP
+.B dovecot
+will log all of its activities according to syslog.
+.SH OPTIONS
+.TP 12
+.BI \-c " \<config file\>"
+This option changes the default configuration file in which 
+.B dovecot 
+looks for its configuration.
+.
+.TP 12
+.BI \-F 
+.B dovecot
+will run in the foreground, and it will still log to syslog.
+.TP
+.BI \--version
+Print version information and exits.
+
+.SH FILES
+.TP 12
+.I /etc/dovecot/dovecot.conf
+Default configuration file.
+.TP 12
+.I /etc/dovecot/dovecot-ldap.conf
+Additional configuration for LDAP based authentication.
+.TP 12
+.I /etc/dovecot/dovecot-pgsql.conf
+Additional configuration for Postgres based authentication.
+.TP 12
+.I /etc/dovecot/dovecot-mysql.conf
+Additional configuration for MySQL based authentication.
+.TP 12
+.I /usr/lib/dovecot.
+Location of 
+.B dovecot 
+modules.
+
+.SH DEBIAN-SPECIFIC
+.B dovecot
+is split into three packages:
+.IP "\(bu" 4
+.B dovecot-common
+contains files used by both servers
+.IP "\(bu" 4
+.B dovecot-imapd
+contains the IMAP server
+.IP "\(bu" 4
+.B dovecot-pop3d
+contains the POP3 server
+
+.SH AUTHOR
+Timo Sirainen <tss@iki.fi>
+
+.SH COPYRIGHT AND LICENSE
+
+Copyright (c) 2001-2002 Timo Sirainen
+
+Dovecot is mostly licensed under the terms of the LGPL v2.1.  See the file 
+COPYING in the source distribution for exceptions.
+
+This man page is copyright (c) 2004 by Kai Hendry <hendry@iki.fi> who
+wrote it for Debian GNU/Linux.  It may be freely used by others under
+the terms of the BSD license.
+
+.SH "SEE ALSO"
+http://dovecot.org/
+
+The mailing list:
+http://dovecot.org/list/dovecot
+
--- dovecot-1.0.15.orig/debian/dovecot-common.init
+++ dovecot-1.0.15/debian/dovecot-common.init
@@ -0,0 +1,180 @@
+#! /bin/sh
+### BEGIN INIT INFO
+# Provides:          dovecot
+# Required-Start:    $syslog
+# Required-Stop:     $syslog
+# Should-Start:      $local_fs $time ntp
+# Should-Stop:       $local_fs
+# Default-Start:     2 3 4 5
+# Default-Stop:      0 1 6
+# Short-Description: Dovecot init script
+# Description:       Init script for dovecot services
+### END INIT INFO
+
+# Author: Miquel van Smoorenburg <miquels@cistron.nl>.
+#         Modified for Debian GNU/Linux
+#         by Ian Murdock <imurdock@gnu.ai.mit.edu>.
+#
+
+# Do NOT "set -e"
+
+# PATH should only include /usr/* if it runs after the mountnfs.sh script
+PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+DESC="IMAP/POP3 mail server"
+NAME=dovecot
+DAEMON=/usr/sbin/dovecot
+DAEMON_ARGS=""
+SCRIPTNAME=/etc/init.d/$NAME
+CONF=/etc/dovecot/${NAME}.conf
+
+# Read configuration variable file if it is present
+[ -r /etc/default/$NAME ] && . /etc/default/$NAME
+
+# Exit if the package is not installed
+[ -x "$DAEMON" ] || exit 0
+
+# Exit if the configuration file doesn't exist
+[ -f "$CONF" ] || exit 0
+
+# Load the VERBOSE setting and other rcS variables
+. /lib/init/vars.sh
+
+# Define LSB log_* functions.
+# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
+. /lib/lsb/init-functions
+
+# cong file readable?
+if [ ! -r ${CONF} ]; then
+  [ "$VERBOSE" != no ] && log_daemon_msg "${CONF}: not readable" "$NAME" \
+    && log_end_msg 1;
+  exit 1;
+fi
+
+# The init script should do nothing if dovecot or another imap/pop3 server
+# is being run from inetd, and dovecot is configured to run as an imap or
+# pop3 service
+for p in `sed -r "s/^ *(([^:]+|\[[^]]+]|\*):)?(pop3s?|imaps?)[ \t].*/\3/;t;d" \
+  /etc/inetd.conf`
+do
+  for q in `sed -r "s/^[ \t]*protocols[ \t]*=[ \t]*(([^\"]*)|\"(.*)\")/\2\3/;t;d" \
+    ${CONF}`
+  do
+    if [ $p = $q ]; then
+      exit 0
+    fi
+  done
+done
+
+# determine the location of the PID file
+# overide by setting base_dir in conf file or PIDBASE in /etc/defaults/$NAME
+PIDBASE=${PIDBASE:-`sed -r "s/^[ \t]*base_dir[ \t]*=[ \t]*([^ \t]*)/\1/;t;d" ${CONF}`}
+PIDFILE=${PIDBASE:-/var/run/dovecot/}master.pid
+
+#
+# Function that starts the daemon/service
+#
+do_start()
+{
+    # Return
+    #   0 if daemon has been started
+    #   1 if daemon was already running
+    #   2 if daemon could not be started
+    start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
+        || return 1
+    start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
+        $DAEMON_ARGS \
+        || return 2
+}
+
+#
+# Function that stops the daemon/service
+#
+do_stop()
+{
+    # Return
+    #   0 if daemon has been stopped
+    #   1 if daemon was already stopped
+    #   2 if daemon could not be stopped
+    #   other if a failure occurred
+    start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name ${DAEMON##*/}
+    RETVAL="$?"
+    [ "$RETVAL" = 2 ] && return 2
+    # Wait for children to finish too if this is a daemon that forks
+    # and if the daemon is only ever run from this initscript.
+    # If the above conditions are not satisfied then add some other code
+    # that waits for the process to drop all resources that could be
+    # needed by services started subsequently.  A last resort is to
+    # sleep for some time.
+    start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --pidfile $PIDFILE --name ${DAEMON##*/}
+    [ "$?" = 2 ] && return 2
+    # Many daemons don't delete their pidfiles when they exit.
+    rm -f $PIDFILE
+    return "$RETVAL"
+}
+
+#
+# Function that sends a SIGHUP to the daemon/service
+#
+do_reload() {
+    #
+    # If the daemon can reload its configuration without
+    # restarting (for example, when it is sent a SIGHUP),
+    # then implement that here.
+    #
+    start-stop-daemon --stop --signal USR1 --quiet --pidfile $PIDFILE --name $NAME
+    return 0
+}
+
+
+case "$1" in
+  start)
+    [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
+    do_start
+    case "$?" in
+        0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+        2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+    esac
+    ;;
+  stop)
+    [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
+    do_stop
+    case "$?" in
+        0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+        2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+    esac
+    ;;
+  reload|force-reload)
+    log_daemon_msg "Reloading $DESC" "$NAME"
+    do_reload
+    log_end_msg $?
+    ;;
+  restart)
+    #
+    # If the "reload" option is implemented then remove the
+    # 'force-reload' alias
+    #
+    log_daemon_msg "Restarting $DESC" "$NAME"
+    do_stop
+    case "$?" in
+      0|1)
+        do_start
+        case "$?" in
+            0) log_end_msg 0 ;;
+            1) log_end_msg 1 ;; # Old process is still running
+            *) log_end_msg 1 ;; # Failed to start
+        esac
+        ;;
+      *)
+        # Failed to stop
+        log_end_msg 1
+        ;;
+    esac
+    ;;
+  *)
+    #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
+    echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
+    exit 3
+    ;;
+esac
+
+exit 0
--- dovecot-1.0.15.orig/debian/dovecot-imapd.NEWS.Debian
+++ dovecot-1.0.15/debian/dovecot-imapd.NEWS.Debian
@@ -0,0 +1,10 @@
+dovecot (0.99.13-1) unstable; urgency=low
+
+  * As of this version, dovecot-imapd doesn't make changes to
+    /etc/dovecot/dovecot.conf.  If this is your first time installing the
+    package, read /usr/share/doc/dovecot-imapd/README.Debian to learn how
+    to enable the server.
+
+  -- Jaldhar H. Vyas <jaldhar@debian.org>  Fri, 31 Dec 2004 15:53:16 -0500
+
+
--- dovecot-1.0.15.orig/debian/dovecot-imapd.README.Debian
+++ dovecot-1.0.15/debian/dovecot-imapd.README.Debian
@@ -0,0 +1,13 @@
+First time Installs
+-------------------
+
+dovecot-imapd will not actually start up until the protocols= line in 
+/etc/dovecot/dovecot.conf contains one or both of the keywords imap (for
+IMAP4rev1 on standard port 143) or imaps (for IMAP4rev1 over SSL on standard 
+port 993.)
+
+After making the change, do: 
+
+  # invoke-rc.d dovecot restart
+
+to enable imapd. 
--- dovecot-1.0.15.orig/debian/dovecot-common.dirs
+++ dovecot-1.0.15/debian/dovecot-common.dirs
@@ -0,0 +1,8 @@
+etc
+etc/ssl/certs
+etc/ssl/private
+usr/sbin
+usr/share/dovecot
+usr/share/lintian/overrides
+var/run/dovecot
+var/run/dovecot/login
--- dovecot-1.0.15.orig/debian/dovecot-imapd.dirs
+++ dovecot-1.0.15/debian/dovecot-imapd.dirs
@@ -0,0 +1 @@
+usr/lib/dovecot
--- dovecot-1.0.15.orig/debian/dovecot-pop3d.postrm
+++ dovecot-1.0.15/debian/dovecot-pop3d.postrm
@@ -0,0 +1,20 @@
+#!/bin/sh
+set -e
+
+
+if [ "$1" = "remove" ]; then
+	# Remove the imaps and imap option from the protocols line
+	perl -pi.bak -e 'if (/^\s*protocols =/i) { s/pop3s//; s/pop3//; s/$/ none/ unless (/imap/ or /none/); s/[ \t]+/ /g; };'\
+   /etc/dovecot/dovecot.conf
+fi
+
+# Restart dovecot because we've updated the configuration file.
+if [ -x "/etc/init.d/dovecot" ]; then
+	if [ -x /usr/sbin/invoke-rc.d ] ; then
+		invoke-rc.d dovecot start
+	else
+		/etc/init.d/dovecot start
+	fi
+fi
+
+#DEBHELPER#
--- dovecot-1.0.15.orig/debian/dovecot-pop3d.postinst
+++ dovecot-1.0.15/debian/dovecot-pop3d.postinst
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+set -e
+
+if [ "$1" = "configure" -a -z "$2" ]; then
+	# Add the pop3 and pop3s options to the protocols line.
+	perl -pi.bak -e 'if (/^\s*protocols =/i) { s/none//; s/$/ pop3 pop3s/ unless /pop3/;  s/[ \t]+/ /g; };'\
+	/etc/dovecot/dovecot.conf
+fi
+
+if [ -x "/etc/init.d/dovecot" ]; then
+	if [ -x /usr/sbin/invoke-rc.d ]; then
+		invoke-rc.d dovecot restart
+	else
+		/etc/init.d/dovecot restart
+	fi
+fi
+
+#DEBHELPER#
--- dovecot-1.0.15.orig/debian/copyright
+++ dovecot-1.0.15/debian/copyright
@@ -0,0 +1,155 @@
+This package was debianized by Jaldhar H. Vyas <jaldhar@debian.org> on
+Tue,  3 Dec 2002 01:10:07 -0500.
+
+It was downloaded from http://www.dovecot.org/
+
+The sieve dovecot plugin, contained in the directory dovecot-sieve,
+has been exported from CVS since there is no official release on
+www.dovecot.org.
+
+Upstream Author: Timo Sirainen <tss@iki.fi>
+
+Copyright:
+    Copyright (c) 2001-2002 Timo Sirainen
+
+Everything is LGPLv2.1 unless otherwise mentioned at the
+beginning of the file. Almost everything in src/lib/ is with MIT license.
+
+The exceptions are:
+
+src/lib/
+  - md5.c : Public Domain
+  - base64.c, mkgmtime.c : BSD-like (read it)
+  - hash.c, primes.c, strfuncs.c, tree.c : LGPL v2
+
+src/lib-imap/imap-match.c : BSD-like (read it)
+
+The BSD, and LGPL can be found in /usr/share/common-licenses on a Debian
+system.  The MIT license is as follows:
+
+    Copyright (c) 2001-2002 Timo Sirainen
+
+    Permission is hereby granted, free of charge, to any person obtaining
+    a copy of this software and associated documentation files (the
+    "Software"), to deal in the Software without restriction, including
+    without limitation the rights to use, copy, modify, merge, publish,
+    distribute, sublicense, and/or sell copies of the Software, and to
+    permit persons to whom the Software is furnished to do so, subject to
+    the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+src/lib/sha1.c, sha2.c : BSD license
+
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+
+ * Copyright (C) 2005, 2007 Olivier Gay <olivier.gay@a3.epfl.ch>
+ * All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+
+src/lib/base64.c, utc-mktime.c :
+
+ * Copyright (c) 2000 Carnegie Mellon University.  All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any other legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ * Copyright (c) 1987, 1989, 1993
+ *      The Regents of the University of California.  All rights reserved.
+
+ * This code is derived from software contributed to Berkeley by
+ * Arthur David Olson of the National Cancer Institute.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by the University of
+ *      California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
