commit-mailutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[SCM] GNU Mailutils branch, master, updated. mu-1-2-90-separate-argp-cfg


From: Sergey Poznyakoff
Subject: [SCM] GNU Mailutils branch, master, updated. mu-1-2-90-separate-argp-cfg-247-g43088a1
Date: Tue, 24 Mar 2009 18:56:22 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU Mailutils".

http://git.savannah.gnu.org/cgit/mailutils.git/commit/?id=43088a1c350b70b627edeb98d927beca3bcd7708

The branch, master has been updated
       via  43088a1c350b70b627edeb98d927beca3bcd7708 (commit)
       via  303823bda827fcf3a6f570afe4a9ad7b637663b4 (commit)
       via  128efb9c9b268678f477fb39aa427c7c939019a7 (commit)
      from  37fcd5c14e6e258cda738c99bdf4cad51bc63643 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 43088a1c350b70b627edeb98d927beca3bcd7708
Author: Sergey Poznyakoff <address@hidden>
Date:   Tue Mar 24 20:56:07 2009 +0200

    Rewrite scripting support in maidag.
    
    * include/mailutils/guile.h (mu_guile_init, mu_guile_load)
    (mu_guile_eval, mu_guile_mailbox_apply, mu_guile_message_apply)
    (mu_guile_safe_exec, mu_guile_safe_proc_call): New protos.
    
    * libmu_scm/Makefile.am: Add mu_guile.c
    * libmu_scm/mu_guile.c: New file.
    * libmu_scm/mu_mailbox.c (struct mu_mailbox): Kludge: new member noclose.
    (mu_scm_mailbox_free): Do not close/destroy mailbox if it is marked
    noclose.
    (mu_scm_mailbox_create0): Kludge: new function.
    
    * libmu_scm/mu_message.c (mu_scm_message_print): Bugfix.
    
    * maidag/Makefile.am: Add guile.c and sieve.c.
    * maidag/deliver.c (maidag_stdio_delivery) [WITH_GUILE]: Remove block.
    (deliver_url): Call script_apply, instead of sieve_test.
    * maidag/maidag.c: Rewrite scripting support in a modular way.
    Remove options: --sieve, --source (and the corresponding config statements).
    Add options: --language, --script.
    Add configure statement: filter (block).
    * maidag/maidag.h (progfile_pattern, sieve_pattern): Remove.
    (script_list, sieve_debug_flags, message_id_header, sieve_enable_log): New
    prototypes.
    [WITH_GUILE]: Remove.
    (maidag_script_fun, struct maidag_script): New data type.
    (script_handler): New extern.
    (script_lang_handler, script_suffix_handler)
    (script_register, script_apply): New protos.
    (scheme_check_msg, sieve_check_msg): New protos.
    * maidag/script.c: Rewrite. Provide general-purpose serialized script 
support.
    
    * guimb/guimb.h (struct guimb_data): Remove.
    * guimb/main.c: Rewrite in a cleaner way, using functions from mu_guile.c.
    * guimb/collect.c (guimb_catch_body, guimb_catch_handler)
    (guimb_exit): Remove.
    
    * guimb/scm/sieve-core.scm (sieve-current-message, sieve-mailbox): Public.
    (sieve-run-current-message): New public function.
    (sieve-run): Call sieve-run-current-message for each message.
    * guimb/scm/sieve.scm.in (sieve-save-program): Change code generation to
    suit both per-mailbox and per-message invocation. This kicks mail.local
    out of whack.
    
    * maidag/guile.c: New file.
    * maidag/sieve.c: New file.

commit 303823bda827fcf3a6f570afe4a9ad7b637663b4
Author: Sergey Poznyakoff <address@hidden>
Date:   Tue Mar 24 13:27:30 2009 +0200

    Minor fixes
    
    * configure.ac (GUIMB): Rename to MU_SCM_BIN_PROGRAMS_BUILD.
    * guimb/Makefile.am: Likewise.

commit 128efb9c9b268678f477fb39aa427c7c939019a7
Author: Sergey Poznyakoff <address@hidden>
Date:   Mon Mar 23 21:26:55 2009 +0200

    Fix imap4d input tokenizer.
    
    The proper delimiters are ( and ), listed in RFC as atom_specials.
    The characters "[]<>." are delimiters for FETCH, and "." is  a
    delimiter for STORE.  Fix tokenizer to recognize these only in the
    corresponding contexts.
    
    In particular, it fixes parsing of input atoms containing dots, as
    address@hidden, which previous versions incorrectly splitted in three.
    
    * imap4d/util.c (ISDELIM): Delimiters are only ( and )
    * imap4d/imap4d.h (struct imap4d_parsebuf): new data type.
    (imap4d_parsebuf_t): New data type.
    (imap4d_parsebuf_exit, imap4d_parsebuf_peek)
    (imap4d_parsebuf_next, imap4d_parsebuf_token)
    (imap4d_parsebuf_data, imap4d_with_parsebuf): New prototypes.
    * imap4d/parsebuf.c: New file.
    * imap4d/Makefile.am: Add parsebuf.c
    * imap4d/fetch.c, imap4d/store.c: Rewrite using parsebuf functions.

-----------------------------------------------------------------------

Summary of changes:
 configure.ac                |    4 +-
 guimb/Makefile.am           |    7 +-
 guimb/collect.c             |   30 +----
 guimb/guimb.h               |   11 +--
 guimb/main.c                |   68 ++++++----
 guimb/scm/sieve-core.scm    |   17 ++-
 guimb/scm/sieve.scm.in      |   83 +++++++-----
 imap4d/Makefile.am          |    3 +-
 imap4d/fetch.c              |  321 +++++++++++++++++++++----------------------
 imap4d/imap4d.h             |   30 ++++-
 imap4d/parsebuf.c           |  175 +++++++++++++++++++++++
 imap4d/store.c              |  223 +++++++++++++++---------------
 imap4d/util.c               |   13 ++-
 include/mailutils/guile.h   |   12 ++-
 libmu_scm/Makefile.am       |    1 +
 libmu_scm/mu_guile.c        |  199 ++++++++++++++++++++++++++
 libmu_scm/mu_mailbox.c      |   32 ++++-
 libmu_scm/mu_message.c      |    2 +-
 libmu_scm/mu_scm.h          |    1 -
 maidag/Makefile.am          |    2 +
 maidag/deliver.c            |   17 +--
 mail/cd.c => maidag/guile.c |   37 +++---
 maidag/maidag.c             |  265 ++++++++++++++---------------------
 maidag/maidag.h             |   48 +++++--
 maidag/script.c             |  212 ++++++++++++++++-------------
 maidag/sieve.c              |  115 +++++++++++++++
 26 files changed, 1227 insertions(+), 701 deletions(-)
 create mode 100644 imap4d/parsebuf.c
 create mode 100644 libmu_scm/mu_guile.c
 copy mail/cd.c => maidag/guile.c (63%)
 create mode 100644 maidag/sieve.c

diff --git a/configure.ac b/configure.ac
index 98d669e..bdcb49b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1032,7 +1032,7 @@ fi
 
 dnl Check for Guile
 AC_SUBST(GUILE_BINDIR)
-AC_SUBST(GUIMB)
+AC_SUBST(MU_SCM_BIN_PROGRAMS_BUILD)
 AC_SUBST(MU_SCM_LTLIBRARIES_BUILD)
 AC_SUBST(LIBMU_SCM)
 AC_SUBST(LIBMU_SCM_DEPS)
@@ -1048,7 +1048,7 @@ if test "$useguile" != "no"; then
          [useguile=yes
          AC_DEFINE(WITH_GUILE,1,[Enable Guile support])
           GUILE_BINDIR=`guile-config info bindir`
-          GUIMB='guimb$(EXEEXT)'  # FIXME
+         MU_SCM_BIN_PROGRAMS_BUILD='$(MU_SCM_BIN_PROGRAMS_LIST)'
           MU_SCM_LTLIBRARIES_BUILD='$(MU_SCM_LTLIBRARIES_LIST)'
           LIBMU_SCM=../libmu_scm/libmu_scm.la
           LIBMU_SCM_DEPS='${MU_LIB_MBOX} ${MU_LIB_IMAP} ${MU_LIB_POP} 
${MU_LIB_MH} ${MU_LIB_MAILDIR} ${MU_LIB_MAILER}'
diff --git a/guimb/Makefile.am b/guimb/Makefile.am
index 2c452e2..ebfd475 100644
--- a/guimb/Makefile.am
+++ b/guimb/Makefile.am
@@ -1,6 +1,6 @@
 ## Process this file with GNU Automake to create Makefile.in
 
-##   Copyright (C) 2001, 2002, 2007 Free Software Foundation, Inc.
+##   Copyright (C) 2001, 2002, 2007, 2009 Free Software Foundation, Inc.
 ##
 ##   GNU Mailutils is free software; you can redistribute it and/or
 ##   modify it under the terms of the GNU General Public License as
@@ -21,8 +21,9 @@ INCLUDES = @MU_COMMON_INCLUDES@ @GUILE_INCLUDES@
 
 SUBDIRS=scm
 
address@hidden@
-EXTRA_PROGRAMS=guimb
+MU_SCM_BIN_PROGRAMS_LIST=guimb # FIXME: does it need $(EXEEXT)?
address@hidden@
+EXTRA_PROGRAMS=$(MU_SCM_BIN_PROGRAMS_LIST)
 guimb_SOURCES=main.c collect.c util.c guimb.h 
 guimb_LDADD = \
  ${MU_APP_LIBRARIES}\
diff --git a/guimb/collect.c b/guimb/collect.c
index d4f78f9..8839d98 100644
--- a/guimb/collect.c
+++ b/guimb/collect.c
@@ -1,6 +1,6 @@
 /* GNU Mailutils -- a suite of utilities for electronic mail
    Copyright (C) 1999, 2000, 2001, 2002, 2005, 
-   2007 Free Software Foundation, Inc.
+   2007, 2009 Free Software Foundation, Inc.
 
    GNU Mailutils is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -191,31 +191,3 @@ collect_drop_mailbox ()
     }
 }
 
-SCM
-guimb_catch_body (void *data, mu_mailbox_t unused)
-{
-  struct guimb_data *gd = data;
-  if (gd->program_file)
-    scm_primitive_load (scm_makfrom0str (gd->program_file));
-
-  if (gd->program_expr)
-    scm_c_eval_string (gd->program_expr);
-
-  return SCM_BOOL_F;
-}
-
-SCM
-guimb_catch_handler (void *unused, SCM tag, SCM throw_args)
-{
-  collect_drop_mailbox ();
-  return scm_handle_by_message ("guimb", tag, throw_args);
-}
-
-int
-guimb_exit (void *unused1, mu_mailbox_t unused2)
-{
-  int rc = collect_output ();
-  collect_drop_mailbox ();
-  return rc;
-}
-
diff --git a/guimb/guimb.h b/guimb/guimb.h
index 95df141..166f86c 100644
--- a/guimb/guimb.h
+++ b/guimb/guimb.h
@@ -1,6 +1,6 @@
 /* GNU Mailutils -- a suite of utilities for electronic mail
    Copyright (C) 1999, 2000, 2001, 2002, 2005,
-   2007 Free Software Foundation, Inc.
+   2007, 2009 Free Software Foundation, Inc.
 
    GNU Mailutils is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -67,12 +67,3 @@ int collect_output (void);
 void util_error (const char *fmt, ...) MU_PRINTFLIKE(1, 2);
 int util_tempfile (char **namep);
 
-struct guimb_data
-{
-  char *program_file;
-  char *program_expr;
-};
-
-SCM guimb_catch_body (void *data, mu_mailbox_t unused);
-SCM guimb_catch_handler (void *unused, SCM tag, SCM throw_args);
-int guimb_exit (void *unused1, mu_mailbox_t unused2);
diff --git a/guimb/main.c b/guimb/main.c
index 64c548b..6a29472 100644
--- a/guimb/main.c
+++ b/guimb/main.c
@@ -145,19 +145,21 @@ static const char *guimb_argp_capa[] = {
   "license",
   NULL
 };
-    
+
+const char *main_sym = "mailutils-main";
+
 int
 main (int argc, char *argv[])
 {
+  int rc;
   int c = argc;
   int index;
-  mu_guimb_param_t param;
-  struct guimb_data gd;
 
   /* Native Language Support */
   MU_APP_INIT_NLS ();
 
-  append_arg ("");
+  /* Register the desired formats. */
+  mu_register_all_formats ();
 
   mu_argp_init (program_version, NULL);
   if (mu_app_init (&argp, guimb_argp_capa, NULL, argc, argv, 0, &index, &c))
@@ -169,17 +171,12 @@ main (int argc, char *argv[])
   if (!user_name)
     user_name = who_am_i ();
   
-  if (program_file)
-    g_argv[0] = program_file;
-  else if (!program_expr)
+  if (!program_file && !program_expr)
     {
       mu_error (_("At least one of -fecs must be used. Try guimb --help for 
more info."));
-      exit (0);
+      exit (1);
     }
     
-  /* Register the desired formats. */
-  mu_register_all_formats ();
-
   if (!argv[index])
     {
       if (default_mailbox)
@@ -202,27 +199,42 @@ main (int argc, char *argv[])
        collect_append_file ("-");
     }
 
-  append_arg (NULL);
-  g_argc--;
-
   /* Finish creating input mailbox */
   collect_create_mailbox ();
 
-  gd.program_file = program_file;
-  gd.program_expr = program_expr;
+  mu_guile_init (debug_guile);
+  if (program_file)
+    mu_guile_load (program_file, g_argc, g_argv);
+  if (program_expr)
+    mu_guile_eval (program_expr);
+
+  rc = mu_guile_mailbox_apply (mbox, main_sym);
+  switch (rc)
+    {
+    case 0:
+      collect_output ();
+      break;
+
+    case MU_ERR_NOENT:
+      mu_error (_("%s not defined"), main_sym);
+      break;
+      
+    case EINVAL:
+      mu_error (_("%s is not a procedure object"), main_sym);
+      break;
+
+    case MU_ERR_FAILURE:
+      mu_error (_("execution of %s failed"), main_sym);
+      break;
+      
+    default:
+      mu_error (_("unrecognized error"));
+      break;
+    }
+
+  collect_drop_mailbox ();
   
-  param.debug_guile = debug_guile;
-  param.mbox = mbox;
-  param.user_name = user_name;
-  param.init = NULL;
-  param.catch_body = guimb_catch_body;
-  param.catch_handler = guimb_catch_handler;
-  param.next = NULL;
-  param.exit = guimb_exit;
-  param.data = &gd;
-  mu_process_mailbox (g_argc, g_argv, &param);
-  /*NOTREACHED*/
-  return 0;
+  return !!rc;
 }
 
 char *
diff --git a/guimb/scm/sieve-core.scm b/guimb/scm/sieve-core.scm
index 89a0872..c75cf34 100644
--- a/guimb/scm/sieve-core.scm
+++ b/guimb/scm/sieve-core.scm
@@ -457,8 +457,15 @@
         (lambda args #f)))
 
 ;;; Sieve-main
-(define sieve-mailbox #f)
-(define sieve-current-message #f)
+(define-public sieve-mailbox #f)
+(define-public sieve-current-message #f)
+
+(define-public (sieve-run-current-message thunk)
+  (and (catch 'sieve-stop
+             thunk
+             (lambda args
+               #f))
+       (sieve-verbose-print "IMPLICIT KEEP")))
 
 (define (sieve-run thunk)
   (if (not sieve-my-email)
@@ -470,11 +477,7 @@
        ((> n count) #f)
        (set! sieve-current-message
              (mu-mailbox-get-message sieve-mailbox n))
-       (and (catch 'sieve-stop
-                   thunk
-                   (lambda args
-                     #f))
-            (sieve-verbose-print "IMPLICIT KEEP")))
+       (sieve-run-current-message thunk))
     (sieve-close-mailboxes)))
 
 (define (sieve-command-line)
diff --git a/guimb/scm/sieve.scm.in b/guimb/scm/sieve.scm.in
index 4a8672f..293dcf7 100644
--- a/guimb/scm/sieve.scm.in
+++ b/guimb/scm/sieve.scm.in
@@ -1,8 +1,9 @@
 #! %GUILE_BINDIR%/guile -s
-# Emacs, its -*- scheme -*-
+# Emacs, it's -*- scheme -*-
 !#
 ;;;; GNU Mailutils -- a suite of utilities for electronic mail
-;;;; Copyright (C) 1999, 2000, 2001, 2006, 2007 Free Software Foundation, Inc.
+;;;; Copyright (C) 1999, 2000, 2001, 2006, 2007,
+;;;; 2009 Free Software Foundation, Inc.
 ;;;;
 ;;;; GNU Mailutils is free software; you can redistribute it and/or modify
 ;;;; it under the terms of the GNU General Public License as published by
@@ -931,39 +932,53 @@
       outfile
     (lambda ()
       (display "#! ")
-      (if guimb-header
-        (display "/home/gray/alpha/bin/guimb -s\n")
-        (display "/usr/bin/guile -s\n"))
-     (display (string-append
-              "# Guile mailbox parser made from " filename))
-     (newline) 
-     (display "# by sieve.scm, GNU %PACKAGE% %VERSION%\n!#")
-     (newline)
-
-     (display 
-      "(if (not (member \"%GUILE_SITE%\" %load-path))\n
+      (cond
+       (guimb-header
+       (display "/home/gray/alpha/bin/guimb -s\n"))
+       (else
+       (display "/bin/sh\n\
+# aside from this initial boilerplate, this is actually -*- scheme -*- code\n\
+exec ${GUILE-guile} -l $0 -c '(mailutils-main)'\n")))
+      (display (string-append
+               "# This Guile mailbox parser was made from " filename))
+      (newline) 
+      (display "# by sieve.scm, GNU %PACKAGE% %VERSION%\n!#")
+      (newline)
+      
+      (display 
+       "(if (not (member \"%GUILE_SITE%\" %load-path))\n
            (set! %load-path (cons \"%GUILE_SITE%\" %load-path)))\n")
-     
-     (display "(use-modules (mailutils sieve-core))\n")
-     (display (string-append
-              "(set! sieve-source \"" filename "\")"))
-     (newline)
-
-     (for-each
-      (lambda (file)
-       (display (string-append
-                 "(load \"" file "\")"))
-       (newline))
-      sieve-load-files)
-     (newline)
-     (if request-verbose
-        (display "(set! sieve-verbose #t)\n"))
-     (display "(sieve-main ")
-
-     (sieve-code-print-list
-      (append '(lambda ())
-             sieve-code-list))
-     (display ")"))))
+      
+      (display "(use-modules (mailutils sieve-core))\n")
+      (display (string-append
+               "(set! sieve-source \"" filename "\")"))
+      (newline)
+      
+      (for-each
+       (lambda (file)
+        (display (string-append
+                  "(load \"" file "\")"))
+        (newline))
+       sieve-load-files)
+      (newline)
+      (if request-verbose
+         (display "(set! sieve-verbose #t)\n"))
+      (display "(define (sieve-filter-thunk) ")
+      
+      (sieve-code-print-list (car sieve-code-list))
+      (display ")\n\n")
+
+      (display "(define (mailutils-main . rest)\n")
+      (display "  (sieve-main sieve-filter-thunk))\n\n")
+
+      (display "(define (mailutils-check-message msg)\n\
+  (set! sieve-current-message msg)\n\
+  (sieve-run-current-message sieve-filter-thunk))\n")
+      
+      (display "\n\
+;;;; Local Variables:\n\
+;;;; buffer-read-only: t\n\
+;;;; End:\n"))))
 
 ;;;;
 
diff --git a/imap4d/Makefile.am b/imap4d/Makefile.am
index 5c0a720..751daf3 100644
--- a/imap4d/Makefile.am
+++ b/imap4d/Makefile.am
@@ -1,7 +1,7 @@
 ## Process this file with GNU Automake to create Makefile.in
 
 ##   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2005,
-##   2007, 2008 Free Software Foundation, Inc.
+##   2007, 2008, 2009 Free Software Foundation, Inc.
 ##
 ##   GNU Mailutils is free software; you can redistribute it and/or
 ##   modify it under the terms of the GNU General Public License as
@@ -47,6 +47,7 @@ imap4d_SOURCES = \
  lsub.c\
  namespace.c\
  noop.c\
+ parsebuf.c\
  preauth.c\
  rename.c\
  search.c\
diff --git a/imap4d/fetch.c b/imap4d/fetch.c
index c90337e..141acc9 100644
--- a/imap4d/fetch.c
+++ b/imap4d/fetch.c
@@ -1,6 +1,6 @@
 /* GNU Mailutils -- a suite of utilities for electronic mail
    Copyright (C) 1999, 2001, 2005, 2006, 2007,
-   2008 Free Software Foundation, Inc.
+   2008, 2009 Free Software Foundation, Inc.
 
    GNU Mailutils is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -58,6 +58,14 @@ struct fetch_function_closure
   size_t size;                     /* Substring length */
 };
 
+struct fetch_parse_closure
+{
+  int isuid;
+  mu_list_t fnlist;
+  size_t *set;
+  size_t count;
+};
+
 
 static int
 fetch_send_address (const char *addr)
@@ -1086,42 +1094,9 @@ _do_fetch (void *item, void *data)
     util_send (" ");
   return ffc->fun (ffc, frt);
 }
-    
-struct parsebuf
-{
-  imap4d_tokbuf_t tok;
-  int arg;
-  char *token;
-  int isuid;
-  mu_list_t fnlist;
-  jmp_buf errjmp;
-  char *err_text;
-};
 
 static void
-parsebuf_exit (struct parsebuf *p, char *text)
-{
-  p->err_text = text;
-  longjmp (p->errjmp, 1);
-}
-
-static char *
-parsebuf_peek (struct parsebuf *p)
-{
-  return imap4d_tokbuf_getarg (p->tok, p->arg);
-}
-
-static char *
-parsebuf_next (struct parsebuf *p, int req)
-{
-  p->token = imap4d_tokbuf_getarg (p->tok, p->arg++);
-  if (!p->token && req)
-    parsebuf_exit (p, "Too few arguments");
-  return p->token;
-}
-
-static void
-append_ffc (struct parsebuf *p, struct fetch_function_closure *ffc)
+append_ffc (struct fetch_parse_closure *p, struct fetch_function_closure *ffc)
 {
   struct fetch_function_closure *new_ffc = malloc (sizeof (*new_ffc));
   if (!new_ffc)
@@ -1131,7 +1106,7 @@ append_ffc (struct parsebuf *p, struct 
fetch_function_closure *ffc)
 }
 
 static void
-append_simple_function (struct parsebuf *p, const char *name,
+append_simple_function (struct fetch_parse_closure *p, const char *name,
                        fetch_function_t fun)
 {
   struct fetch_function_closure ffc;
@@ -1199,12 +1174,12 @@ fetch-att       = "ENVELOPE" / "FLAGS" / "INTERNALDATE" 
/
 
 /*  "RFC822" [".HEADER" / ".SIZE" / ".TEXT"]  */
 static void
-parse_fetch_rfc822 (struct parsebuf *p)
+parse_fetch_rfc822 (imap4d_parsebuf_t p)
 {
   struct fetch_function_closure ffc;
   ffc_init (&ffc);
   ffc.name = "RFC822";
-  parsebuf_next (p, 0);
+  imap4d_parsebuf_next (p, 0);
   if (p->token == NULL || p->token[0] == ')') 
     {
       /* Equivalent to BODY[]. */
@@ -1212,7 +1187,7 @@ parse_fetch_rfc822 (struct parsebuf *p)
     }
   else if (p->token[0] == '.')
     {
-      parsebuf_next (p, 1);
+      imap4d_parsebuf_next (p, 1);
       if (strcasecmp (p->token, "HEADER") == 0)
        {
          /* RFC822.HEADER
@@ -1225,14 +1200,14 @@ parse_fetch_rfc822 (struct parsebuf *p)
          ffc.name = "RFC822.HEADER";
          ffc.fun = _frt_header;
          ffc.peek = 1;
-         parsebuf_next (p, 0);
+         imap4d_parsebuf_next (p, 0);
        }
       else if (strcasecmp (p->token, "SIZE") == 0)
        {
          /* A number expressing the [RFC-2822] size of the message. */
          ffc.name = "RFC822.SIZE";
          ffc.fun = _frt_size;
-         parsebuf_next (p, 0);
+         imap4d_parsebuf_next (p, 0);
        }
       else if (strcasecmp (p->token, "TEXT") == 0)
        {
@@ -1240,14 +1215,14 @@ parse_fetch_rfc822 (struct parsebuf *p)
             Equivalent to BODY[TEXT]. */
          ffc.name = "RFC822.TEXT";
          ffc.fun = _frt_body_text;
-         parsebuf_next (p, 0);
+         imap4d_parsebuf_next (p, 0);
        }
       else
-       parsebuf_exit (p, "Syntax error after RFC822.");
+       imap4d_parsebuf_exit (p, "Syntax error after RFC822.");
     }
   else
-    parsebuf_exit (p, "Syntax error after RFC822");
-  append_ffc (p, &ffc);
+    imap4d_parsebuf_exit (p, "Syntax error after RFC822");
+  append_ffc (imap4d_parsebuf_data (p), &ffc);
 }
 
 static int
@@ -1262,19 +1237,19 @@ header-fld-name = astring
 header-list     = "(" header-fld-name *(SP header-fld-name) ")"
 */
 static void
-parse_header_list (struct parsebuf *p, struct fetch_function_closure *ffc)
+parse_header_list (imap4d_parsebuf_t p, struct fetch_function_closure *ffc)
 {
   if (!(p->token && p->token[0] == '('))
-    parsebuf_exit (p, "Syntax error: expected (");
+    imap4d_parsebuf_exit (p, "Syntax error: expected (");
   mu_list_create (&ffc->headers);
   mu_list_set_comparator (ffc->headers, _header_cmp);
-  for (parsebuf_next (p, 1); p->token[0] != ')'; parsebuf_next (p, 1))
+  for (imap4d_parsebuf_next (p, 1); p->token[0] != ')'; imap4d_parsebuf_next 
(p, 1))
     {
-      if (util_isdelim (p->token))
-       parsebuf_exit (p, "Syntax error: unexpected delimiter");
+      if (p->token[1] == 0 && strchr ("()[]<>.", p->token[0]))
+       imap4d_parsebuf_exit (p, "Syntax error: unexpected delimiter");
       mu_list_append (ffc->headers, p->token);
     }
-  parsebuf_next (p, 1);
+  imap4d_parsebuf_next (p, 1);
 }
 
 /*
@@ -1285,30 +1260,30 @@ section-text    = section-msgtext / "MIME"
                     ; text other than actual body part (headers, etc.)
 */  
 static int
-parse_section_text (struct parsebuf *p, struct fetch_function_closure *ffc,
+parse_section_text (imap4d_parsebuf_t p, struct fetch_function_closure *ffc,
                    int allow_mime)
 {
   if (strcasecmp (p->token, "HEADER") == 0)
     {
       /* "HEADER" / "HEADER.FIELDS" [".NOT"] SP header-list  */
-      parsebuf_next (p, 1);
+      imap4d_parsebuf_next (p, 1);
       if (p->token[0] == '.')
        {
-         parsebuf_next (p, 1);
+         imap4d_parsebuf_next (p, 1);
          if (strcasecmp (p->token, "FIELDS"))
-           parsebuf_exit (p, "Expected FIELDS");
+           imap4d_parsebuf_exit (p, "Expected FIELDS");
          ffc->fun = _frt_header_fields;
-         parsebuf_next (p, 1);
+         imap4d_parsebuf_next (p, 1);
          if (p->token[0] == '.')
            {
-             parsebuf_next (p, 1);
+             imap4d_parsebuf_next (p, 1);
              if (strcasecmp (p->token, "NOT") == 0)
                {
                  ffc->not = 1;
-                 parsebuf_next (p, 1);
+                 imap4d_parsebuf_next (p, 1);
                }
              else
-               parsebuf_exit (p, "Expected NOT");
+               imap4d_parsebuf_exit (p, "Expected NOT");
            }
          parse_header_list (p, ffc);
        }
@@ -1317,12 +1292,12 @@ parse_section_text (struct parsebuf *p, struct 
fetch_function_closure *ffc,
     }
   else if (strcasecmp (p->token, "TEXT") == 0)
     {
-      parsebuf_next (p, 1);
+      imap4d_parsebuf_next (p, 1);
       ffc->fun = _frt_body_text;
     }
   else if (allow_mime && strcasecmp (p->token, "MIME") == 0)
     {
-      parsebuf_next (p, 1);
+      imap4d_parsebuf_next (p, 1);
       ffc->fun = _frt_mime;
     }
   else
@@ -1331,13 +1306,13 @@ parse_section_text (struct parsebuf *p, struct 
fetch_function_closure *ffc,
 }
 
 static size_t
-parsebuf_get_number (struct parsebuf *p)
+parsebuf_get_number (imap4d_parsebuf_t p)
 {
   char *cp;
   unsigned n = strtoul (p->token, &cp, 10);
 
   if (*cp)
-    parsebuf_exit (p, "Syntax error: expected number");
+    imap4d_parsebuf_exit (p, "Syntax error: expected number");
   return n;
 }
     
@@ -1346,7 +1321,7 @@ section-part    = nz-number *("." nz-number)
                     ; body part nesting
 */  
 static void
-parse_section_part (struct parsebuf *p, struct fetch_function_closure *ffc)
+parse_section_part (imap4d_parsebuf_t p, struct fetch_function_closure *ffc)
 {
   size_t *parts;
   size_t nmax = 0;
@@ -1373,10 +1348,12 @@ parse_section_part (struct parsebuf *p, struct 
fetch_function_closure *ffc)
        }
       parts[ncur++] = n;
 
-      parsebuf_next (p, 1);
+      imap4d_parsebuf_next (p, 1);
+      
       if (p->token[0] == '.'
-         && (cp = parsebuf_peek (p)) && isascii (*cp) && isdigit (cp[0]))
-       parsebuf_next (p, 1);
+         && (cp = imap4d_parsebuf_peek (p))
+         && isascii (*cp) && isdigit (*cp))
+       imap4d_parsebuf_next (p, 1);
       else
        break;
     }
@@ -1389,14 +1366,14 @@ section         = "[" [section-spec] "]"
 section-spec    = section-msgtext / (section-part ["." section-text])
 */  
 static int
-parse_section (struct parsebuf *p, struct fetch_function_closure *ffc)
+parse_section (imap4d_parsebuf_t p, struct fetch_function_closure *ffc)
 {
   if (p->token[0] != '[')
     return 1;
   ffc_init (ffc);
   ffc->name = NULL;
   ffc->fun = _frt_body;
-  parsebuf_next (p, 1);
+  imap4d_parsebuf_next (p, 1);
   if (parse_section_text (p, ffc, 0))
     {
       if (p->token[0] == ']')
@@ -1406,98 +1383,102 @@ parse_section (struct parsebuf *p, struct 
fetch_function_closure *ffc)
          parse_section_part (p, ffc);
          if (p->token[0] == '.')
            {
-             parsebuf_next (p, 1);
+             imap4d_parsebuf_next (p, 1);
              parse_section_text (p, ffc, 1);
            }
        }
       else
-       parsebuf_exit (p, "Syntax error");
+       imap4d_parsebuf_exit (p, "Syntax error");
     }
   if (p->token[0] != ']')
-    parsebuf_exit (p, "Syntax error: missing ]");
-  parsebuf_next (p, 0);
+    imap4d_parsebuf_exit (p, "Syntax error: missing ]");
+  imap4d_parsebuf_next (p, 0);
   return 0;
 }
 
 static void
-parse_substring (struct parsebuf *p, struct fetch_function_closure *ffc)
+parse_substring (imap4d_parsebuf_t p, struct fetch_function_closure *ffc)
 {
   if (p->token && p->token[0] == '<')
     {
-      parsebuf_next (p, 1);
+      imap4d_parsebuf_next (p, 1);
       ffc->start = parsebuf_get_number (p);
-      parsebuf_next (p, 1);
+      imap4d_parsebuf_next (p, 1);
       if (p->token[0] != '.')
-       parsebuf_exit (p, "Syntax error: expected .");
-      parsebuf_next (p, 1);
+       imap4d_parsebuf_exit (p, "Syntax error: expected .");
+      imap4d_parsebuf_next (p, 1);
       ffc->size = parsebuf_get_number (p);
-      parsebuf_next (p, 1);
+      imap4d_parsebuf_next (p, 1);
       if (p->token[0] != '>')
-       parsebuf_exit (p, "Syntax error: expected >");
-      parsebuf_next (p, 0);
+       imap4d_parsebuf_exit (p, "Syntax error: expected >");
+      imap4d_parsebuf_next (p, 0);
     }
 }
        
 /* section ["<" number "." nz-number ">"]  */
 static int
-parse_body_args (struct parsebuf *p, int peek)
+parse_body_args (imap4d_parsebuf_t p, int peek)
 {
   struct fetch_function_closure ffc;
   if (parse_section (p, &ffc) == 0)
     {
       parse_substring (p, &ffc);
       ffc.peek = peek;
-      append_ffc (p, &ffc);
+      append_ffc (imap4d_parsebuf_data (p), &ffc);
       return 0;
     }
   return 1;
 }
 
 static void
-parse_body_peek (struct parsebuf *p)
+parse_body_peek (imap4d_parsebuf_t p)
 {
-  parsebuf_next (p, 1);
+  imap4d_parsebuf_next (p, 1);
   if (strcasecmp (p->token, "PEEK") == 0)
     {
-      parsebuf_next (p, 1);
+      imap4d_parsebuf_next (p, 1);
       if (parse_body_args (p, 1))
-       parsebuf_exit (p, "Syntax error");
+       imap4d_parsebuf_exit (p, "Syntax error");
     }
   else
-    parsebuf_exit (p, "Syntax error: expected PEEK");
+    imap4d_parsebuf_exit (p, "Syntax error: expected PEEK");
 }
 
 /*  "BODY" ["STRUCTURE"] / 
     "BODY" section ["<" number "." nz-number ">"] /
     "BODY.PEEK" section ["<" number "." nz-number ">"] */
 static void
-parse_fetch_body (struct parsebuf *p)
+parse_fetch_body (imap4d_parsebuf_t p)
 {
-  if (parsebuf_next (p, 0) == NULL || p->token[0] == ')')
-    append_simple_function (p, "BODY", _frt_bodystructure0);
+  if (imap4d_parsebuf_next (p, 0) == NULL || p->token[0] == ')')
+    append_simple_function (imap4d_parsebuf_data (p),
+                           "BODY", _frt_bodystructure0);
   else if (p->token[0] == '.')
     parse_body_peek (p);
   else if (strcasecmp (p->token, "STRUCTURE") == 0)
     {
       /* For compatibility with previous versions */
-      append_simple_function (p, "BODYSTRUCTURE", _frt_bodystructure);
-      parsebuf_next (p, 0);
+      append_simple_function (imap4d_parsebuf_data (p),
+                             "BODYSTRUCTURE", _frt_bodystructure);
+      imap4d_parsebuf_next (p, 0);
     }
   else if (parse_body_args (p, 0))
-    append_simple_function (p, "BODY", _frt_bodystructure0);
+    append_simple_function (imap4d_parsebuf_data (p),
+                           "BODY", _frt_bodystructure0);
 }
 
 static int
-parse_fetch_att (struct parsebuf *p)
+parse_fetch_att (imap4d_parsebuf_t p)
 {
   struct fetch_att_tab *ent;
-
+  struct fetch_parse_closure *pclos = imap4d_parsebuf_data (p);
+  
   ent = find_fetch_att_tab (p->token);
   if (ent)
     {
-      if (!(ent->fun == _frt_uid && p->isuid))
-       append_simple_function (p, ent->name, ent->fun);
-      parsebuf_next (p, 0);
+      if (!(ent->fun == _frt_uid && pclos->isuid))
+       append_simple_function (pclos, ent->name, ent->fun);
+      imap4d_parsebuf_next (p, 0);
     }
   else if (strcasecmp (p->token, "RFC822") == 0)
     parse_fetch_rfc822 (p);
@@ -1505,8 +1486,8 @@ parse_fetch_att (struct parsebuf *p)
     parse_fetch_body (p);
   else if (strcasecmp (p->token, "BODYSTRUCTURE") == 0)
     {
-      append_simple_function (p, "BODYSTRUCTURE", _frt_bodystructure);
-      parsebuf_next (p, 0);
+      append_simple_function (pclos, "BODYSTRUCTURE", _frt_bodystructure);
+      imap4d_parsebuf_next (p, 0);
     }
   else
     return 1;
@@ -1515,7 +1496,7 @@ parse_fetch_att (struct parsebuf *p)
 
 /* fetch-att *(SP fetch-att) */
 static void
-parse_fetch_att_list (struct parsebuf *p)
+parse_fetch_att_list (imap4d_parsebuf_t p)
 {
   while (p->token && parse_fetch_att (p) == 0)
     ;
@@ -1523,17 +1504,17 @@ parse_fetch_att_list (struct parsebuf *p)
 
 /* "ALL" / "FULL" / "FAST" / fetch-att / "(" */
 static void
-parse_macro (struct parsebuf *p)
+parse_macro (imap4d_parsebuf_t p)
 {  
   char *exp;
   
-  parsebuf_next (p, 1);
+  imap4d_parsebuf_next (p, 1);
   if (p->token[0] == '(')
     {
-      parsebuf_next (p, 1);
+      imap4d_parsebuf_next (p, 1);
       parse_fetch_att_list (p);
       if (!(p->token && p->token[0] == ')'))
-       parsebuf_exit (p, "Unknown token or missing closing parenthesis");
+       imap4d_parsebuf_exit (p, "Unknown token or missing closing 
parenthesis");
     }
   else if ((exp = find_macro (p->token))) 
     {
@@ -1541,63 +1522,43 @@ parse_macro (struct parsebuf *p)
       int save_arg = p->arg;
       p->tok = imap4d_tokbuf_from_string (exp);
       p->arg = 0;
-      parsebuf_next (p, 1);
+      imap4d_parsebuf_next (p, 1);
       parse_fetch_att_list (p);
       imap4d_tokbuf_destroy (&p->tok);
   
       p->arg = save_arg;
       p->tok = save_tok;
 
-      if (parsebuf_peek (p))
-       parsebuf_exit (p, "Too many arguments");
+      if (imap4d_parsebuf_peek (p))
+       imap4d_parsebuf_exit (p, "Too many arguments");
     }     
   else
     {
       parse_fetch_att (p);
       if (p->token)
-       parsebuf_exit (p, "Too many arguments");
+       imap4d_parsebuf_exit (p, "Too many arguments");
     }
 }
     
-/* Where the real implementation is.  It is here since UID command also
-   calls FETCH.  */
-int
-imap4d_fetch0 (imap4d_tokbuf_t tok, int isuid, char **err_text)
+
+static int
+fetch_thunk (imap4d_parsebuf_t pb)
 {
-  int rc = RESP_OK;
-  char *msgset;
-  size_t *set = NULL;
-  int n = 0;
-  int i;
   int status;
-  struct fetch_runtime_closure frc;
-  struct parsebuf pb;
-
-  if (imap4d_tokbuf_argc (tok) - (IMAP4_ARG_1 + isuid) < 2)
-    {
-      *err_text = "Invalid arguments";
-      return 1;
-    }
-  
-  pb.tok = tok;
-  pb.arg = IMAP4_ARG_1 + isuid;
-  pb.isuid = isuid;
-  pb.err_text = "Syntax error";
-  mu_list_create (&pb.fnlist);
-  mu_list_set_destroy_item (pb.fnlist, _free_ffc);
-  if (setjmp (pb.errjmp))
-    {
-      *err_text = pb.err_text;
-      mu_list_destroy (&pb.fnlist);
-      return RESP_BAD;
-    }
+  char *msgset;
+  struct fetch_parse_closure *pclos = imap4d_parsebuf_data (pb);
   
-  msgset = parsebuf_next (&pb, 1);
+  msgset = imap4d_parsebuf_next (pb, 1);
 
   /* Get the message numbers in set[].  */
-  status = util_msgset (msgset, &set, &n, isuid);
-  if (status != 0)
+  status = util_msgset (msgset, &pclos->set, &pclos->count, pclos->isuid);
+  switch (status)
     {
+    case 0:
+      /* Very good! */
+      break;
+      
+    case EINVAL:
       /* RFC3501, section 6.4.8.
         
         A non-existent unique identifier is ignored without any error
@@ -1609,8 +1570,12 @@ imap4d_fetch0 (imap4d_tokbuf_t tok, int isuid, char 
**err_text)
         as well, although I did not find any explicit mention thereof
         in the RFC. */
       
-      *err_text = "Completed";
+      pb->err_text = "Completed";
       return RESP_OK;
+
+    default:
+      pb->err_text = "Failed to parse message set";
+      return RESP_NO;
     }
 
   /* Compile the expression */
@@ -1619,33 +1584,67 @@ imap4d_fetch0 (imap4d_tokbuf_t tok, int isuid, char 
**err_text)
      include the UID message data item as part of any FETCH
      response caused by a UID command, regardless of whether
      a UID was specified as a message data item to the FETCH. */
-  if (isuid)
-    append_simple_function (&pb, "UID", _frt_uid);
+  if (pclos->isuid)
+    append_simple_function (pclos, "UID", _frt_uid);
 
-  parse_macro (&pb);
-  
-  /* Prepare status code. It will be replaced if an error occurs in the
-     loop below */
-  frc.err_text = "Completed";
+  parse_macro (pb);
+  return RESP_OK;
+}
+
+/* Where the real implementation is.  It is here since UID command also
+   calls FETCH.  */
+int
+imap4d_fetch0 (imap4d_tokbuf_t tok, int isuid, char **err_text)
+{
+  int rc;
+  struct fetch_parse_closure pclos;
   
-  for (i = 0; i < n && rc == RESP_OK; i++)
+  if (imap4d_tokbuf_argc (tok) - (IMAP4_ARG_1 + isuid) < 2)
     {
-      frc.msgno = (isuid) ? uid_to_msgno (set[i]) : set[i];
+      *err_text = "Invalid arguments";
+      return 1;
+    }
+
+  memset (&pclos, 0, sizeof (pclos));
+  pclos.isuid = isuid;
+  mu_list_create (&pclos.fnlist);
+  mu_list_set_destroy_item (pclos.fnlist, _free_ffc);
+  
+  rc = imap4d_with_parsebuf (tok, IMAP4_ARG_1 + isuid,
+                            ".[]<>",
+                            fetch_thunk, &pclos,
+                            err_text);
 
-      if (frc.msgno && mu_mailbox_get_message (mbox, frc.msgno, &frc.msg) == 0)
+  if (rc == RESP_OK)
+    {
+      size_t i;
+      struct fetch_runtime_closure frc;
+      
+      /* Prepare status code. It will be replaced if an error occurs in the
+        loop below */
+      frc.err_text = "Completed";
+  
+      for (i = 0; i < pclos.count && rc == RESP_OK; i++)
        {
-         util_send ("* %lu FETCH (", (unsigned long) frc.msgno);
-         frc.eltno = 0;
-         rc = mu_list_do (pb.fnlist, _do_fetch, &frc);
-         util_send (")\r\n");
+         frc.msgno = (isuid) ? uid_to_msgno (pclos.set[i]) : pclos.set[i];
+         
+         if (frc.msgno &&
+             mu_mailbox_get_message (mbox, frc.msgno, &frc.msg) == 0)
+           {
+             util_send ("* %lu FETCH (", (unsigned long) frc.msgno);
+             frc.eltno = 0;
+             rc = mu_list_do (pclos.fnlist, _do_fetch, &frc);
+             util_send (")\r\n");
+           }
        }
-    }
+      }
   
-  mu_list_destroy (&pb.fnlist);
-  free (set);
+  mu_list_destroy (&pclos.fnlist);
+  free (pclos.set);
   return rc;
 }
 
+
 /*
 6.4.5.  FETCH Command
 
diff --git a/imap4d/imap4d.h b/imap4d/imap4d.h
index c054884..c2705e3 100644
--- a/imap4d/imap4d.h
+++ b/imap4d/imap4d.h
@@ -1,6 +1,6 @@
 /* GNU Mailutils -- a suite of utilities for electronic mail
    Copyright (C) 1999, 2001, 2002, 2003, 2004, 
-   2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+   2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
 
    GNU Mailutils is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -218,7 +218,35 @@ int imap4d_getline (char **pbuf, size_t *psize, size_t 
*pnbytes);
 #define IMAP4_ARG_2       3
 #define IMAP4_ARG_3       4
 #define IMAP4_ARG_4       5
+
+  /* Specialized buffer parsing */
+struct imap4d_parsebuf
+{
+  imap4d_tokbuf_t tok;
+  int arg;            /* Argument number in tok */
+  char *tokptr;       /* Current token */
+  size_t tokoff;      /* Offset of next delimiter in tokptr */
+  int save_char;      /* Character saved from tokptr[tokoff] */
+  char *token;        /* Pointer to the current token */
+  const char *delim;  /* Array of delimiters */
+  char *peek_ptr;     /* Peeked token */
+  jmp_buf errjmp;
+  char *err_text;
+  void *data;
+};
+
+typedef struct imap4d_parsebuf *imap4d_parsebuf_t;
   
+void imap4d_parsebuf_exit (imap4d_parsebuf_t pb, char *text);
+char *imap4d_parsebuf_peek (imap4d_parsebuf_t pb);
+char *imap4d_parsebuf_next (imap4d_parsebuf_t pb, int req);
+int imap4d_with_parsebuf (imap4d_tokbuf_t tok, int arg,
+                         const char *delim,
+                         int (*thunk) (imap4d_parsebuf_t), void *data,
+                         char **err_text);
+#define imap4d_parsebuf_token(p) ((p)->token)
+#define imap4d_parsebuf_data(p) ((p)->data)
+
   /* Imap4 commands */
 extern int  imap4d_append (struct imap4d_command *, imap4d_tokbuf_t);
 extern int  imap4d_authenticate (struct imap4d_command *, imap4d_tokbuf_t);
diff --git a/imap4d/parsebuf.c b/imap4d/parsebuf.c
new file mode 100644
index 0000000..f43d68a
--- /dev/null
+++ b/imap4d/parsebuf.c
@@ -0,0 +1,175 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   GNU Mailutils is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GNU Mailutils 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GNU Mailutils; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+   MA 02110-1301 USA */
+
+#include "imap4d.h"
+
+static void
+parsebuf_free (struct imap4d_parsebuf *p)
+{
+  if (p->peek_ptr)
+    free (p->peek_ptr);
+}
+  
+void
+imap4d_parsebuf_exit (struct imap4d_parsebuf *p, char *text)
+{
+  p->err_text = text;
+  longjmp (p->errjmp, 1);
+}
+
+static char *
+pbcopy (const char *str, size_t len)
+{
+  char *p = malloc (len + 1);
+  if (!p)
+    imap4d_bye (ERR_NO_MEM);
+  memcpy (p, str, len);
+  p[len] = 0;
+  return p;
+}
+
+static char *
+pbcopy_char (int c)
+{
+  char ch = c;
+  return pbcopy (&ch, 1);
+}
+
+char *
+imap4d_parsebuf_peek (struct imap4d_parsebuf *p)
+{
+  if (!p->peek_ptr)
+    {
+      if (!p->tokptr || p->save_char == 0)
+       {
+         char *token = imap4d_tokbuf_getarg (p->tok, p->arg);
+         if (!token)
+           return NULL;
+         if (p->delim)
+           {
+             if (strchr (p->delim, token[0]))
+               p->peek_ptr = pbcopy_char (token[0]);
+             else
+               p->peek_ptr = pbcopy (token, strcspn (token, p->delim));
+           }
+         else
+           p->peek_ptr = pbcopy (token, strlen (token));
+       }
+      else
+       {
+         char *token = p->token + p->tokoff;
+
+         if (strchr (p->delim, p->save_char))
+           p->peek_ptr = pbcopy_char (p->save_char);
+         else
+           {
+             size_t off = strcspn (token + 1, p->delim);
+             p->peek_ptr = pbcopy (token, off + 1);
+             p->peek_ptr[0] = p->save_char;
+           }
+       }
+    }
+  return p->peek_ptr;
+}
+
+char *
+imap4d_parsebuf_next (struct imap4d_parsebuf *p, int req)
+{
+  if (p->peek_ptr)
+    {
+      free (p->peek_ptr);
+      p->peek_ptr = NULL;
+    }
+
+  if (!p->tokptr || p->save_char == 0)
+    {
+      p->tokptr = imap4d_tokbuf_getarg (p->tok, p->arg++);
+      p->token = p->tokptr;
+      p->tokoff = 0;
+      if (!p->token)
+       {
+         if (req)
+           imap4d_parsebuf_exit (p, "Too few arguments");
+         return NULL;
+       }
+      if (p->delim)
+       {
+         if (strchr (p->delim, p->token[0]))
+           {
+             p->save_char = p->token[1];
+             p->tokoff = 1;
+             p->token[1] = 0;
+           }
+         else
+           {
+             p->tokoff = strcspn (p->token, p->delim);
+             if ((p->save_char = p->token[p->tokoff]))
+               p->token[p->tokoff] = 0;
+           }
+       }
+      else
+       p->save_char = 0;
+    }
+  else
+    {
+      p->token[p->tokoff] = p->save_char;
+      p->token += p->tokoff;
+      if (strchr (p->delim, p->save_char))
+       {
+         p->save_char = p->token[1];
+         p->token[1] = 0;
+         p->tokoff = 1;
+       }
+      else
+       {
+         p->tokoff = strcspn (p->token, p->delim);
+         if ((p->save_char = p->token[p->tokoff]))
+           p->token[p->tokoff] = 0;
+       }
+    }
+  
+  return p->token;
+}
+
+int
+imap4d_with_parsebuf (imap4d_tokbuf_t tok, int arg, const char *delim,
+                     int (*thunk) (imap4d_parsebuf_t), void *data,
+                     char **err_text)
+{
+  struct imap4d_parsebuf pbuf;
+  int rc;
+  
+  memset (&pbuf, 0, sizeof pbuf);
+  pbuf.tok = tok;
+  pbuf.arg = arg;
+  pbuf.delim = delim;
+  pbuf.data = data;
+  pbuf.err_text = "Syntax error";
+  pbuf.data = data;
+  
+  if (setjmp (pbuf.errjmp))
+    {
+      *err_text = pbuf.err_text;
+      parsebuf_free (&pbuf);
+      return RESP_BAD;
+    }
+
+  rc = thunk (&pbuf);
+  parsebuf_free (&pbuf);
+  return rc;
+}
diff --git a/imap4d/store.c b/imap4d/store.c
index 5f3af09..c47c482 100644
--- a/imap4d/store.c
+++ b/imap4d/store.c
@@ -1,5 +1,6 @@
 /* GNU Mailutils -- a suite of utilities for electronic mail
-   Copyright (C) 1999, 2001, 2005, 2007, 2008 Free Software Foundation, Inc.
+   Copyright (C) 1999, 2001, 2005, 2007, 2008,
+   2009 Free Software Foundation, Inc.
 
    GNU Mailutils is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -18,165 +19,165 @@
 
 #include "imap4d.h"
 
-/*
- * Now you're messing with a sumbitch
- */
+enum value_type { STORE_SET, STORE_ADD, STORE_UNSET };
 
-int
-imap4d_store (struct imap4d_command *command, imap4d_tokbuf_t tok)
+struct store_parse_closure
 {
-  int rc;
-  char *err_text;
-  
-  rc = imap4d_store0 (tok, 0, &err_text);
-  return util_finish (command, rc, "%s", err_text);
-}
-
-struct parsebuf
-{
-  char *token;
-  imap4d_tokbuf_t tok;
-  int arg;
-  jmp_buf errjmp;
-  char *err_text;
+  enum value_type how;
+  int ack;
+  int type;
+  int isuid;
+  size_t *set;
+  size_t count;
 };
-
-static void
-parsebuf_exit (struct parsebuf *p, char *text)
-{
-  p->err_text = text;
-  longjmp (p->errjmp, 1);
-}
-
-static char *
-parsebuf_next (struct parsebuf *p, int req)
-{
-  p->token = imap4d_tokbuf_getarg (p->tok, p->arg++);
-  if (!p->token && req)
-    parsebuf_exit (p, "Too few arguments");
-  return p->token;
-}
-
-int
-imap4d_store0 (imap4d_tokbuf_t tok, int isuid, char **ptext)
+  
+static int
+store_thunk (imap4d_parsebuf_t p)
 {
+  struct store_parse_closure *pclos = imap4d_parsebuf_data (p);
   char *msgset;
-  int status;
-  int ack = 1;
-  size_t i;
-  int n = 0;
-  size_t *set = NULL;
-  enum value_type { STORE_SET, STORE_ADD, STORE_UNSET } how;
-  struct parsebuf pb;
   char *data;
-  int type = 0;
+  int status;
   
-  pb.tok = tok;
-  pb.arg = IMAP4_ARG_1 + !!isuid;
-  pb.err_text = NULL;
-  if (setjmp (pb.errjmp))
-    {
-      *ptext = pb.err_text;
-      free (set);
-      return RESP_BAD;
-    }
-      
-  msgset = parsebuf_next (&pb, 1);
-  data = parsebuf_next (&pb, 1);
+  msgset = imap4d_parsebuf_next (p, 1);
+  data = imap4d_parsebuf_next (p, 1);
 
   if (*data == '+')
     {
-      how = STORE_ADD;
+      pclos->how = STORE_ADD;
       data++;
     }
   else if (*data == '-')
     {
-      how = STORE_UNSET;
+      pclos->how = STORE_UNSET;
       data++;
     }
   else
-    how = STORE_SET;
+    pclos->how = STORE_SET;
   
   if (strcasecmp (data, "FLAGS"))
-    parsebuf_exit (&pb, "Bogus data item");
-  data = parsebuf_next (&pb, 1);
+    imap4d_parsebuf_exit (p, "Bogus data item");
+  data = imap4d_parsebuf_next (p, 1);
 
   if (*data == '.')
     {
-      data = parsebuf_next (&pb, 1);
+      data = imap4d_parsebuf_next (p, 1);
       if (strcasecmp (data, "SILENT") == 0)
        {
-         ack = 0;
-         parsebuf_next (&pb, 1);
+         pclos->ack = 0;
+         imap4d_parsebuf_next (p, 1);
        }
       else
-       parsebuf_exit (&pb, "Bogus data suffix");
+       imap4d_parsebuf_exit (p, "Bogus data suffix");
     }
 
   /* Get the message numbers in set[].  */
-  status = util_msgset (msgset, &set, &n, isuid);
-  if (status != 0)
+  status = util_msgset (msgset, &pclos->set, &pclos->count, pclos->isuid);
+  switch (status)
     {
+    case 0:
+      break;
+
+    case EINVAL:
       /* See RFC 3501, section 6.4.8, and a comment to the equivalent code
         in fetch.c */
-      *ptext = "Completed";
+      p->err_text = "Completed";
       return RESP_OK;
+
+    default:
+      p->err_text = "Failed to parse message set";
+      return RESP_NO;
     }      
 
-  if (pb.token[0] != '(')
-    parsebuf_exit (&pb, "Syntax error");
-  parsebuf_next (&pb, 1);
+  if (p->token[0] != '(')
+    imap4d_parsebuf_exit (p, "Syntax error");
+  imap4d_parsebuf_next (p, 1);
   
   do
     {
       int t;
-      if (!util_attribute_to_type (pb.token, &t))
-       type |= t;
+      if (!util_attribute_to_type (p->token, &t))
+       pclos->type |= t;
     }
-  while (parsebuf_next (&pb, 1) && pb.token[0] != ')');
+  while (imap4d_parsebuf_next (p, 1) && p->token[0] != ')');
+  return RESP_OK;
+}
+
+int
+imap4d_store0 (imap4d_tokbuf_t tok, int isuid, char **ptext)
+{
+  int rc;
+  struct store_parse_closure pclos;
 
-  for (i = 0; i < n; i++)
+  memset (&pclos, 0, sizeof pclos);
+  pclos.ack = 1;
+  pclos.isuid = isuid;
+  
+  rc = imap4d_with_parsebuf (tok,
+                            IMAP4_ARG_1 + !!isuid,
+                            ".",
+                            store_thunk, &pclos,
+                            ptext);
+  if (rc == RESP_OK)
     {
-      mu_message_t msg = NULL;
-      mu_attribute_t attr = NULL;
-      size_t msgno = isuid ? uid_to_msgno (set[i]) : set[i];
+      size_t i;
       
-      if (msgno)
+      for (i = 0; i < pclos.count; i++)
        {
-         mu_mailbox_get_message (mbox, msgno, &msg);
-         mu_message_get_attribute (msg, &attr);
-
-         switch (how)
-           {
-           case STORE_ADD:
-             mu_attribute_set_flags (attr, type);
-             break;
+         mu_message_t msg = NULL;
+         mu_attribute_t attr = NULL;
+         size_t msgno = isuid ? uid_to_msgno (pclos.set[i]) : pclos.set[i];
       
-           case STORE_UNSET:
-             mu_attribute_unset_flags (attr, type);
-             break;
+         if (msgno)
+           {
+             mu_mailbox_get_message (mbox, msgno, &msg);
+             mu_message_get_attribute (msg, &attr);
+             
+             switch (pclos.how)
+               {
+               case STORE_ADD:
+                 mu_attribute_set_flags (attr, pclos.type);
+                 break;
+                 
+               case STORE_UNSET:
+                 mu_attribute_unset_flags (attr, pclos.type);
+                 break;
       
-           case STORE_SET:
-             mu_attribute_unset_flags (attr, 0xffffffff); /* FIXME */
-             mu_attribute_set_flags (attr, type);
+               case STORE_SET:
+                 mu_attribute_unset_flags (attr, 0xffffffff); /* FIXME */
+                 mu_attribute_set_flags (attr, pclos.type);
+               }
+           }
+         
+         if (pclos.ack)
+           {
+             util_send ("* %d FETCH (", msgno);
+             
+             if (isuid)
+               util_send ("UID %lu ", (unsigned long) msgno);
+             util_send ("FLAGS (");
+             util_print_flags (attr);
+             util_send ("))\r\n");
            }
+         /* Update the flags of uid table.  */
+         imap4d_sync_flags (pclos.set[i]);
        }
 
-      if (ack)
-       {
-         util_send ("* %d FETCH (", msgno);
-
-         if (isuid)
-           util_send ("UID %lu ", (unsigned long) msgno);
-         util_send ("FLAGS (");
-         util_print_flags (attr);
-         util_send ("))\r\n");
-       }
-      /* Update the flags of uid table.  */
-      imap4d_sync_flags (set[i]);
+      *ptext = "Completed";
     }
-  free (set);
-  *ptext = "Completed";
-  return RESP_OK;
+
+  free (pclos.set);
+  
+  return rc;
+}
+
+int
+imap4d_store (struct imap4d_command *command, imap4d_tokbuf_t tok)
+{
+  int rc;
+  char *err_text;
+  
+  rc = imap4d_store0 (tok, 0, &err_text);
+  return util_finish (command, rc, "%s", err_text);
 }
 
diff --git a/imap4d/util.c b/imap4d/util.c
index 47b1f08..f63fc90 100644
--- a/imap4d/util.c
+++ b/imap4d/util.c
@@ -1,6 +1,6 @@
 /* GNU Mailutils -- a suite of utilities for electronic mail
    Copyright (C) 1999, 2001, 2002, 2003, 2004, 
-   2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+   2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
 
    GNU Mailutils is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -121,6 +121,7 @@ util_msgset (char *s, size_t ** set, int *n, int isuid)
              {
                if (*set)
                  free (*set);
+               *set = NULL;
                *n = 0;
                return EINVAL;
              }
@@ -133,6 +134,7 @@ util_msgset (char *s, size_t ** set, int *n, int isuid)
                  }
                if (*set)
                  free (*set);
+               *set = NULL;
                *n = 0;
                return EINVAL;
              }
@@ -147,6 +149,7 @@ util_msgset (char *s, size_t ** set, int *n, int isuid)
                    if (tmp < 0 || val == 0)
                      {
                        free (*set);
+                       *set = NULL;
                        *n = 0;
                        return EINVAL;
                      }
@@ -206,6 +209,7 @@ util_msgset (char *s, size_t ** set, int *n, int isuid)
          done = 1;
          if (*set)
            free (*set);
+         *set = NULL;
          *n = 0;
          return EINVAL;
 
@@ -238,6 +242,7 @@ util_msgset (char *s, size_t ** set, int *n, int isuid)
          if (tmp < 0 || val == 0)
            {
              free (*set);
+             *set = NULL;
              *n = 0;
              return EINVAL;
            }
@@ -1221,7 +1226,7 @@ imap4d_tokbuf_expand (struct imap4d_tokbuf *tok, size_t 
size)
     }
 }
 
-#define ISDELIM(c) (strchr (".()[]<>", (c)) != NULL)
+#define ISDELIM(c) (strchr ("()", (c)) != NULL)
 #define ISWS(c) (c == ' ' || c == '\t')
 
 int
@@ -1257,7 +1262,7 @@ gettok (struct imap4d_tokbuf *tok, size_t off)
        tok->argmax = 16;
       else
        tok->argmax *= 2;
-      tok->argp = realloc(tok->argp, tok->argmax * sizeof (tok->argp[0]));
+      tok->argp = realloc (tok->argp, tok->argmax * sizeof (tok->argp[0]));
       if (!tok->argp)
        imap4d_bye (ERR_NO_MEM);
     }
@@ -1278,7 +1283,7 @@ gettok (struct imap4d_tokbuf *tok, size_t off)
        {
          size_t len;
          off++;
-         len  = unquote(buf + off, p - (buf + off));
+         len  = unquote (buf + off, p - (buf + off));
          buf[off + len] = 0;
          tok->argp[tok->argc++] = off;
          return p - buf + 1;
diff --git a/include/mailutils/guile.h b/include/mailutils/guile.h
index ad03dcf..cdc9c09 100644
--- a/include/mailutils/guile.h
+++ b/include/mailutils/guile.h
@@ -69,10 +69,20 @@ extern SCM mu_port_make_from_stream (SCM msg, mu_stream_t 
stream, long mode);
 extern void mu_scm_mime_init (void);
 extern void mu_scm_message_add_owner (SCM MESG, SCM owner);
 
+extern void mu_scm_mutil_init (void);
+
 extern void mu_process_mailbox (int argc, char *argv[], mu_guimb_param_t 
*param);
 
-extern void mu_scm_mutil_init (void);
+extern void mu_guile_init (int debug);
+extern int mu_guile_load (char *filename, int argc, char **argv);
+extern int mu_guile_eval (const char *string);
+extern int mu_guile_mailbox_apply (mu_mailbox_t mbx, char *funcname);
+extern int mu_guile_message_apply (mu_message_t msg, char *funcname);
 
+extern int mu_guile_safe_exec (SCM (*handler) (void *data), void *data,
+                              SCM *result);
+extern int mu_guile_safe_proc_call (SCM proc, SCM arglist, SCM *presult);
+  
 #ifdef __cplusplus
 }
 #endif
diff --git a/libmu_scm/Makefile.am b/libmu_scm/Makefile.am
index 4a4ecb2..282d262 100644
--- a/libmu_scm/Makefile.am
+++ b/libmu_scm/Makefile.am
@@ -26,6 +26,7 @@ EXTRA_LTLIBRARIES=libmu_scm.la
 C_SRCS=\
  mu_address.c\
  mu_body.c\
+ mu_guile.c\
  mu_guimb.c\
  mu_mailbox.c\
  mu_message.c\
diff --git a/libmu_scm/mu_guile.c b/libmu_scm/mu_guile.c
new file mode 100644
index 0000000..f6b6caa
--- /dev/null
+++ b/libmu_scm/mu_guile.c
@@ -0,0 +1,199 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   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 3 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., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301 USA */
+
+#include "mu_scm.h"
+
+static SCM
+eval_catch_body (void *list)
+{
+  return scm_primitive_eval ((SCM)list);
+}
+
+static SCM
+eval_catch_handler (void *data, SCM tag, SCM throw_args)
+{
+  scm_handle_by_message_noexit ("mailutils", tag, throw_args);
+  longjmp (*(jmp_buf*)data, 1);
+}
+
+struct scheme_exec_data
+{
+  SCM (*handler) (void *data);
+  void *data;
+  SCM result;
+};
+
+static SCM
+scheme_safe_exec_body (void *data)
+{
+  struct scheme_exec_data *ed = data;
+  ed->result = ed->handler (ed->data);
+  return SCM_BOOL_F;
+}
+
+int
+mu_guile_safe_exec (SCM (*handler) (void *data), void *data, SCM *result)
+{
+  jmp_buf jmp_env;
+  struct scheme_exec_data ed;
+       
+  if (setjmp (jmp_env))
+    return 1;
+  ed.handler = handler;
+  ed.data = data;
+  scm_internal_lazy_catch (SCM_BOOL_T,
+                          scheme_safe_exec_body, (void*)&ed,
+                          eval_catch_handler, &jmp_env);
+  if (result)
+    *result = ed.result;
+  return 0;
+}
+
+
+
+SCM
+lookup_handler (void *data)
+{
+  const char *symbol = (const char *)data;
+  return MU_SCM_SYMBOL_VALUE (symbol);
+}
+
+int
+mu_guile_sym_lookup (const char *symbol, SCM *result)
+{
+  return mu_guile_safe_exec (lookup_handler, (void*) symbol, result);
+}
+
+
+int
+mu_guile_safe_proc_call (SCM proc, SCM arglist, SCM *presult)
+{
+  jmp_buf jmp_env;
+  SCM cell, result;
+    
+  if (setjmp (jmp_env))
+    return 1;
+    
+  cell = scm_cons (proc, arglist);
+  result = scm_internal_lazy_catch (SCM_BOOL_T,
+                                   eval_catch_body, cell,
+                                   eval_catch_handler, &jmp_env);
+  if (presult)
+    *presult = result;
+  return 0;
+}
+
+
+
+void
+mu_guile_init (int debug)
+{
+  scm_init_guile ();
+  scm_load_goops ();
+
+  if (debug)
+    {
+      SCM_DEVAL_P = 1;
+      SCM_BACKTRACE_P = 1;
+      SCM_RECORD_POSITIONS_P = 1;
+      SCM_RESET_DEBUG_MODE;
+    }
+  mu_scm_init ();
+}
+
+
+struct load_closure
+{
+  char *filename;
+  int argc;
+  char **argv;
+};
+
+static SCM
+load_path_handler (void *data)
+{
+  struct load_closure *lp = data;
+    
+  scm_set_program_arguments (lp->argc, lp->argv, lp->filename);
+  scm_primitive_load (scm_makfrom0str (lp->filename));
+  return SCM_UNDEFINED;
+}
+
+int
+mu_guile_load (char *filename, int argc, char **argv)
+{
+  struct load_closure lc;
+  lc.filename = filename;
+  lc.argc = argc;
+  lc.argv = argv;
+  return mu_guile_safe_exec (load_path_handler, &lc, NULL);
+}
+
+static SCM
+eval_handler (void *data)
+{
+  const char *string = data;
+  scm_c_eval_string (string);
+  return SCM_UNDEFINED;
+}
+
+int
+mu_guile_eval (const char *string)
+{
+  return mu_guile_safe_exec (eval_handler, (void*) string, NULL);
+}
+
+
+/* See comment on this function in mu_mailbox.c */
+extern SCM mu_scm_mailbox_create0 (mu_mailbox_t mbox, int noclose);
+  
+int
+mu_guile_mailbox_apply (mu_mailbox_t mbx, char *funcname)
+{
+  SCM proc;
+
+  if (mu_guile_sym_lookup (funcname, &proc))
+    return MU_ERR_NOENT;
+  if (scm_procedure_p (proc) != SCM_BOOL_T)
+    return EINVAL;
+
+  if (mu_guile_safe_proc_call (proc,
+                              scm_list_1 (mu_scm_mailbox_create0 (mbx, 1)),
+                              NULL))
+    return MU_ERR_FAILURE;
+
+  return 0;
+}
+
+int
+mu_guile_message_apply (mu_message_t msg, char *funcname)
+{
+  SCM proc;
+
+  if (mu_guile_sym_lookup (funcname, &proc))
+    return MU_ERR_NOENT;
+  if (scm_procedure_p (proc) != SCM_BOOL_T)
+    return EINVAL;
+
+  if (mu_guile_safe_proc_call (proc,
+                      scm_list_1 (mu_scm_message_create (SCM_BOOL_F, msg)),
+                          NULL))
+    return MU_ERR_FAILURE;
+
+  return 0;
+}
diff --git a/libmu_scm/mu_mailbox.c b/libmu_scm/mu_mailbox.c
index 6fb611a..e0a3e47 100644
--- a/libmu_scm/mu_mailbox.c
+++ b/libmu_scm/mu_mailbox.c
@@ -25,6 +25,7 @@ long mailbox_tag;
 struct mu_mailbox
 {
   mu_mailbox_t mbox;       /* Mailbox */
+  int noclose;
 };
 
 /* SMOB functions: */
@@ -38,8 +39,11 @@ static scm_sizet
 mu_scm_mailbox_free (SCM mailbox_smob)
 {
   struct mu_mailbox *mum = (struct mu_mailbox *) SCM_CDR (mailbox_smob);
-  mu_mailbox_close (mum->mbox);
-  mu_mailbox_destroy (&mum->mbox);
+  if (!mum->noclose)
+    {
+      mu_mailbox_close (mum->mbox);
+      mu_mailbox_destroy (&mum->mbox);
+    }
   free (mum);
   /* NOTE: Currently there is no way for this function to return the
      amount of memory *actually freed* by mu_mailbox_destroy */
@@ -85,16 +89,38 @@ mu_scm_mailbox_print (SCM mailbox_smob, SCM port, 
scm_print_state * pstate)
 
 /* Internal functions */
 
+/* There are two C interfaces for creating mailboxes in Scheme.
+   The first one, mu_scm_mailbox_create0, allows to set `noclose'
+   bit, which disables closing and releasing the underlying mu_mailbox_t
+   after the hosting SCM object is freed. Use this, if this mailbox
+   is referenced elsewhere.
+
+   Another one, mu_scm_mailbox_create, always create an object that
+   will cause closing the mu_mailbox_t object and releasing its memory
+   after the hosting SCM object is swept away by GC. This is the only
+   official one.
+
+   The mu_scm_mailbox_create0 function is a kludge, needed because
+   mu_mailbox_t objects don't have reference counters. When it is fixed in
+   the library, the interface will be removed. */
+
 SCM
-mu_scm_mailbox_create (mu_mailbox_t mbox)
+mu_scm_mailbox_create0 (mu_mailbox_t mbox, int noclose)
 {
   struct mu_mailbox *mum;
 
   mum = scm_gc_malloc (sizeof (struct mu_mailbox), "mailbox");
   mum->mbox = mbox;
+  mum->noclose = noclose;
   SCM_RETURN_NEWSMOB (mailbox_tag, mum);
 }
 
+SCM
+mu_scm_mailbox_create (mu_mailbox_t mbox)
+{
+  return mu_scm_mailbox_create0 (mbox, 0);
+}
+
 int
 mu_scm_is_mailbox (SCM scm)
 {
diff --git a/libmu_scm/mu_message.c b/libmu_scm/mu_message.c
index 47f950e..5c73e42 100644
--- a/libmu_scm/mu_message.c
+++ b/libmu_scm/mu_message.c
@@ -103,7 +103,7 @@ mu_scm_message_print (SCM message_smob, SCM port, 
scm_print_state * pstate)
       else
        scm_puts ("UNKNOWN", port);
       
-      if (mu_envelope_sget_date (env, &buffer) == 0
+      if (mu_envelope_sget_date (env, &p) == 0
           && mu_parse_ctime_date_time (&p, &tm, &tz) == 0)
        {
          strftime (datebuf, sizeof (datebuf), "%a %b %e %H:%M", &tm);
diff --git a/libmu_scm/mu_scm.h b/libmu_scm/mu_scm.h
index 92b7b1a..8f04bf5 100644
--- a/libmu_scm/mu_scm.h
+++ b/libmu_scm/mu_scm.h
@@ -49,4 +49,3 @@
 
 #include <mailutils/guile.h>
 
-
diff --git a/maidag/Makefile.am b/maidag/Makefile.am
index 7b5eca2..c19ffc4 100644
--- a/maidag/Makefile.am
+++ b/maidag/Makefile.am
@@ -21,11 +21,13 @@ sbin_PROGRAMS=maidag
 maidag_SOURCES=\
  deliver.c\
  forward.c\
+ guile.c\
  lmtp.c\
  maidag.c\
  maidag.h\
  mailtmp.c\
  mailquota.c\
+ sieve.c\
  script.c\
  util.c
 
diff --git a/maidag/deliver.c b/maidag/deliver.c
index 1c6ae79..e8a5b07 100644
--- a/maidag/deliver.c
+++ b/maidag/deliver.c
@@ -72,19 +72,6 @@ maidag_stdio_delivery (int argc, char **argv)
   if (multiple_delivery)
     multiple_delivery = argc > 1;
 
-#ifdef WITH_GUILE
-  if (progfile_pattern)
-    {
-      struct mda_data mda_data;
-      
-      memset (&mda_data, 0, sizeof mda_data);
-      mda_data.mbox = mbox;
-      mda_data.argv = argv;
-      mda_data.progfile_pattern = progfile_pattern;
-      return prog_mda (&mda_data);
-    }
-#endif
-  
   for (; *argv; argv++)
     mda (mbox, *argv);
   return exit_code;
@@ -322,8 +309,8 @@ deliver_url (mu_url_t url, mu_message_t msg, const char 
*name, char **errp)
 
       if (current_uid)
        auth->change_uid = 0;
-  
-      if (!sieve_test (auth, msg))
+
+      if (script_apply (msg, auth))
        {
          exit_code = EX_OK;
          mu_auth_data_free (auth);
diff --git a/mail/cd.c b/maidag/guile.c
similarity index 63%
copy from mail/cd.c
copy to maidag/guile.c
index b88f46e..9e2b193 100644
--- a/mail/cd.c
+++ b/maidag/guile.c
@@ -1,5 +1,6 @@
 /* GNU Mailutils -- a suite of utilities for electronic mail
-   Copyright (C) 1999, 2005, 2007 Free Software Foundation, Inc.
+   Copyright (C) 1999, 2000, 2001, 2002, 2005,
+   2007, 2009 Free Software Foundation, Inc.
 
    GNU Mailutils is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -16,29 +17,27 @@
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
    MA 02110-1301 USA */
 
-#include "mail.h"
+#include "maidag.h"
 
-/*
- * cd [directory]
- * ch[dir] [directory]
- */
+#ifdef WITH_GUILE
+#include <mailutils/guile.h>
+
+int debug_guile;
+static int initialized;
 
 int
-mail_cd (int argc, char **argv)
+scheme_check_msg (mu_message_t msg, struct mu_auth_data *auth,
+                 const char *prog)
 {
-  char *dir;
-  
-  if (argc > 2)
-    return 1;
-  else if (argc == 2)
-    dir = argv[1];
-  else 
-    dir = getenv ("HOME");
-
-  if (chdir (dir))
+  if (!initialized)
     {
-      mu_error (_("cannot change to `%s': %s"), dir, mu_strerror (errno));
-      return 1;
+      mu_guile_init (debug_guile);
+      initialized = 1;
     }
+  mu_guile_load (prog, 0, NULL);
+  mu_guile_message_apply (msg, "mailutils-check-message");
   return 0;
 }
+
+#endif
+
diff --git a/maidag/maidag.c b/maidag/maidag.c
index 88bc9ab..94aaa03 100644
--- a/maidag/maidag.c
+++ b/maidag/maidag.c
@@ -30,8 +30,10 @@ char *quotadbname = NULL;  /* Name of mailbox quota database 
*/
 char *quota_query = NULL;  /* SQL query to retrieve mailbox quota */
 
 char *sender_address = NULL;       
-char *progfile_pattern = NULL;
-char *sieve_pattern = NULL;
+
+maidag_script_fun script_handler;
+
+mu_list_t script_list;
 
 char *forward_file = NULL;
 int forward_file_checks = FWD_ALL;
@@ -73,28 +75,40 @@ static char args_doc[] = N_("[recipient...]");
 
 static struct argp_option options[] = 
 {
-  { "foreground", FOREGROUND_OPTION, 0, 0, N_("Remain in foreground."), 0 },
-  { "inetd",  'i', 0, 0, N_("Run in inetd mode"), 0 },
+#define GRID 0
+ { NULL, 0, NULL, 0,
+   N_("General options"), GRID },
+      
+  { "foreground", FOREGROUND_OPTION, 0, 0, N_("Remain in foreground."),
+    GRID + 1 },
+  { "inetd",  'i', 0, 0, N_("Run in inetd mode"), GRID + 1 },
   { "daemon", 'd', N_("NUMBER"), OPTION_ARG_OPTIONAL,
-    N_("Runs in daemon mode with a maximum of NUMBER children"), 0 },
-  { "url", URL_OPTION, 0, 0, N_("Deliver to given URLs"), 0 },
+    N_("Runs in daemon mode with a maximum of NUMBER children"), GRID + 1 },
+  { "url", URL_OPTION, 0, 0, N_("Deliver to given URLs"), GRID + 1 },
   { "from", 'f', N_("EMAIL"), 0,
-    N_("Specify the sender's name") },
+    N_("Specify the sender's name"), GRID + 1 },
   { NULL, 'r', NULL, OPTION_ALIAS, NULL },
-  { "sieve", 'S', N_("PATTERN"), 0,
-    N_("Set name pattern for user-defined Sieve mail filters"), 0 },
-  { "message-id-header", MESSAGE_ID_HEADER_OPTION, N_("STRING"), 0,
-    N_("Identify messages by the value of this header when logging Sieve 
actions"), 0 },
-#ifdef WITH_GUILE
-  { "source", 's', N_("PATTERN"), 0,
-    N_("Set name pattern for user-defined Scheme mail filters"), 0 },
-#endif
   { "lmtp", LMTP_OPTION, N_("URL"), OPTION_ARG_OPTIONAL,
-    N_("Operate in LMTP mode"), 0 },
+    N_("Operate in LMTP mode"), GRID + 1 },
   { "debug", 'x', N_("FLAGS"), 0,
-    N_("Enable debugging"), 0 },
+    N_("Enable debugging"), GRID + 1 },
   { "stderr", STDERR_OPTION, NULL, 0,
-    N_("Log to standard error"), 0 },
+    N_("Log to standard error"), GRID + 1 },
+#undef GRID
+
+#define GRID 2
+ { NULL, 0, NULL, 0,
+   N_("Scripting options"), GRID },
+ 
+  { "language", 'l', N_("STRING"), 0,
+    N_("Define scripting language for the next --script option"),
+    GRID + 1 },
+  { "script", 's', N_("PATTERN"), 0,
+    N_("Set name pattern for user-defined mail filter"), GRID + 1 },
+  { "message-id-header", MESSAGE_ID_HEADER_OPTION, N_("STRING"), 0,
+    N_("Use this header to identify messages when logging Sieve actions"),
+    GRID + 1 },
+#undef GRID
   { NULL,      0, NULL, 0, NULL, 0 }
 };
 
@@ -201,21 +215,30 @@ parse_opt (int key, char *arg, struct argp_state *state)
     case 'r':
     case 'f':
       if (sender_address != NULL)
-       {
-         argp_error (state, _("Multiple --from options"));
-         return EX_USAGE;
-       }
+       argp_error (state, _("Multiple --from options"));
       sender_address = arg;
       break;
       
-#ifdef WITH_GUILE      
-    case 's':
-      mu_argp_node_list_new (&lst, "guile-filter", arg);
+    case 'l':
+      script_handler = script_lang_handler (arg);
+      if (!script_handler)
+       argp_error (state, _("Unknown or unsupported language: %s"),
+                   arg);
       break;
-#endif
+      
+    case 's':
+      switch (script_register (arg))
+       {
+       case 0:
+         break;
 
-    case 'S':
-      mu_argp_node_list_new (&lst, "sieve-filter", arg);
+       case EINVAL:
+         argp_error (state, _("%s has unknown file suffix"), arg);
+         break;
+
+       default:
+         argp_error (state, _("error registering script"));
+       }
       break;
       
     case 'x':
@@ -329,6 +352,54 @@ cb_forward_file_checks (mu_debug_t debug, void *data, 
mu_config_value_t *arg)
   return mu_cfg_string_value_cb (debug, arg, cb2_forward_file_checks, data);
 }
 
+static int
+cb_script_language (mu_debug_t debug, void *data, mu_config_value_t *val)
+{
+  if (mu_cfg_assert_value_type (val, MU_CFG_STRING, debug))
+    return 1;
+  script_handler = script_lang_handler (val->v.string);
+  if (!script_handler)
+    {
+      mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+                          _("Unsupported language: %s"),
+                          val->v.string);
+      return 1;
+    }
+  return 0;
+}
+
+static int
+cb_script_pattern (mu_debug_t debug, void *data, mu_config_value_t *val)
+{
+  if (mu_cfg_assert_value_type (val, MU_CFG_STRING, debug))
+    return 1;
+  
+  switch (script_register (val->v.string))
+    {
+    case 0:
+      break;
+
+    case EINVAL:
+      mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+                          _("%s has unknown file suffix"),
+                          val->v.string);
+      break;
+
+    default:
+      mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+                          _("error registering script"));
+    }
+  return 0;
+}
+
+struct mu_cfg_param filter_cfg_param[] = {
+  { "language", mu_cfg_callback, NULL, 0, cb_script_language,
+    N_("Set script language.") },
+  { "pattern", mu_cfg_callback, NULL, 0, cb_script_pattern,
+    N_("Set script pattern.") },
+  { NULL }
+};
+    
 struct mu_cfg_param maidag_cfg_param[] = {
   { "exit-multiple-delivery-success", mu_cfg_bool, &multiple_delivery, 0, NULL,
     N_("In case of multiple delivery, exit with code 0 if at least one "
@@ -347,18 +418,10 @@ struct mu_cfg_param maidag_cfg_param[] = {
        "sql { ... } instead."),
     N_("query") },
 #endif
-  { "sieve-filter", mu_cfg_string, &sieve_pattern, 0, NULL,
-    N_("File name or name pattern for Sieve filter file."),
-    N_("file-or-pattern") },
   { "message-id-header", mu_cfg_string, &message_id_header, 0, NULL,
     N_("When logging Sieve actions, identify messages by the value of "
        "this header."),
     N_("name") },
-#ifdef WITH_GUILE
-  { "guile-filter", mu_cfg_string, &progfile_pattern, 0, NULL,
-    N_("File name or name pattern for Guile filter file."),
-    N_("file-or-pattern") },
-#endif
   { "debug", mu_cfg_callback, NULL, 0, cb_debug,
     N_("Set maidag debug level.  Debug level consists of one or more "
        "of the following letters:\n"
@@ -388,138 +451,23 @@ struct mu_cfg_param maidag_cfg_param[] = {
     N_("url") },
   { "reuse-address", mu_cfg_bool, &reuse_lmtp_address, 0, NULL,
     N_("Reuse existing address (LMTP mode).  Default is \"yes\".") },
+  { "filter", mu_cfg_section, NULL, 0, NULL,
+    N_("Add a message filter") },
   { ".server", mu_cfg_section, NULL, 0, NULL,
     N_("LMTP server configuration.") },
   TCP_WRAPPERS_CONFIG
   { NULL }
 };
 
-
-/* Logging */
-
-static int
-_sieve_debug_printer (void *unused, const char *fmt, va_list ap)
-{
-  mu_diag_vprintf (MU_DIAG_DEBUG, fmt, ap);
-  return 0;
-}
-
 static void
-_sieve_action_log (void *user_name,
-                  const mu_sieve_locus_t *locus, size_t msgno,
-                  mu_message_t msg,
-                  const char *action, const char *fmt, va_list ap)
-{
-  int pfx = 0;
-  mu_debug_t debug;
-
-  mu_diag_get_debug (&debug);
-  mu_debug_set_locus (debug, locus->source_file, locus->source_line);
-  
-  mu_diag_printf (MU_DIAG_NOTICE, _("(user %s) "), (char*) user_name);
-  if (message_id_header)
-    {
-      mu_header_t hdr = NULL;
-      char *val = NULL;
-      mu_message_get_header (msg, &hdr);
-      if (mu_header_aget_value (hdr, message_id_header, &val) == 0
-         || mu_header_aget_value (hdr, MU_HEADER_MESSAGE_ID, &val) == 0)
-       {
-         pfx = 1;
-         mu_diag_printf (MU_DIAG_NOTICE, _("%s on msg %s"), action, val);
-         free (val);
-       }
-    }
-  
-  if (!pfx)
-    {
-      size_t uid = 0;
-      mu_message_get_uid (msg, &uid);
-      mu_diag_printf (MU_DIAG_NOTICE, _("%s on msg uid %d"), action, uid);
-    }
-  
-  if (fmt && strlen (fmt))
-    {
-      mu_diag_printf (MU_DIAG_NOTICE, "; ");
-      mu_diag_vprintf (MU_DIAG_NOTICE, fmt, ap);
-    }
-  mu_diag_printf (MU_DIAG_NOTICE, "\n");
-  mu_debug_set_locus (debug, NULL, 0);
-}
-
-static int
-_sieve_parse_error (void *user_name, const char *filename, int lineno,
-                   const char *fmt, va_list ap)
-{
-  mu_debug_t debug;
-
-  mu_diag_get_debug (&debug);
-  if (filename)
-    mu_debug_set_locus (debug, filename, lineno);
-
-  mu_diag_printf (MU_DIAG_ERROR, _("(user %s) "), (char*) user_name);
-  mu_diag_vprintf (MU_DIAG_ERROR, fmt, ap);
-  mu_diag_printf (MU_DIAG_ERROR, "\n");
-  mu_debug_set_locus (debug, NULL, 0);
-  return 0;
-}
-
-int
-sieve_test (struct mu_auth_data *auth, mu_message_t msg)
+maidag_cfg_init ()
 {
-  int rc = 1;
-  char *progfile;
-    
-  if (!sieve_pattern)
-    return 1;
-
-  progfile = mu_expand_path_pattern (sieve_pattern, auth->name);
-  if (access (progfile, R_OK))
-    {
-      if (debug_level > 2)
-       mu_diag_output (MU_DIAG_DEBUG, _("Access to %s failed: %m"), progfile);
-    }
-  else
+  struct mu_cfg_section *section;
+  if (mu_create_canned_section ("filter", &section) == 0)
     {
-      mu_sieve_machine_t mach;
-      rc = mu_sieve_machine_init (&mach, auth->name);
-      if (rc)
-       {
-         mu_error (_("Cannot initialize sieve machine: %s"),
-                   mu_strerror (rc));
-       }
-      else
-       {
-         mu_sieve_set_debug (mach, _sieve_debug_printer);
-         mu_sieve_set_debug_level (mach, sieve_debug_flags);
-         mu_sieve_set_parse_error (mach, _sieve_parse_error);
-         if (sieve_enable_log)
-           mu_sieve_set_logger (mach, _sieve_action_log);
-         
-         rc = mu_sieve_compile (mach, progfile);
-         if (rc == 0)
-           {
-             mu_attribute_t attr;
-
-             mu_message_get_attribute (msg, &attr);
-             mu_attribute_unset_deleted (attr);
-             if (switch_user_id (auth, 1) == 0)
-               {
-                 chdir (auth->dir);
-               
-                 rc = mu_sieve_message (mach, msg);
-                 if (rc == 0)
-                   rc = mu_attribute_is_deleted (attr) == 0;
-
-                 switch_user_id (auth, 0);
-                 chdir ("/");
-               }
-             mu_sieve_machine_destroy (&mach);
-           }
-       }
+      section->docstring = N_("Add new message filter.");
+      mu_cfg_section_add_params (section, filter_cfg_param);
     }
-  free (progfile);
-  return rc;
 }
 
 
@@ -557,6 +505,7 @@ main (int argc, char *argv[])
   mu_tcpwrapper_cfg_init ();
   mu_acl_cfg_init ();
   mu_m_server_cfg_init ();
+  maidag_cfg_init ();
   
   /* Parse command line */
   mu_argp_init (program_version, NULL);
diff --git a/maidag/maidag.h b/maidag/maidag.h
index d5806ff..30c6b2b 100644
--- a/maidag/maidag.h
+++ b/maidag/maidag.h
@@ -129,8 +129,10 @@ extern char *forward_file;
 extern int forward_file_checks;
 
 extern char *sender_address;       
-extern char *progfile_pattern;
-extern char *sieve_pattern;
+extern mu_list_t script_list;
+extern char *message_id_header;
+extern int sieve_debug_flags; 
+extern int sieve_enable_log;  
 
 extern mu_m_server_t server;
 extern int lmtp_mode;
@@ -157,20 +159,6 @@ int deliver (mu_message_t msg, char *name, char **errp);
 int sieve_test (struct mu_auth_data *auth, mu_message_t msg);
 int check_quota (struct mu_auth_data *auth, mu_off_t size, mu_off_t *rest);
 
-#ifdef WITH_GUILE
-struct mda_data
-{
-  mu_mailbox_t mbox;
-  char *progfile;
-  char *progfile_pattern;
-  char **argv;
-};
-
-int prog_mda (struct mda_data *data);
-
-extern int debug_guile;
-#endif
-
 struct mail_tmp;
 int mail_tmp_begin (struct mail_tmp **pmtmp, const char *from);
 int mail_tmp_add_line (struct mail_tmp *mtmp, char *buf, size_t buflen);
@@ -188,3 +176,31 @@ enum maidag_forward_result
 enum maidag_forward_result maidag_forward (mu_message_t msg,
                                           struct mu_auth_data *auth,
                                           char *fwfile);
+
+/* Scripting support */
+typedef int (*maidag_script_fun) (mu_message_t msg,
+                                 struct mu_auth_data *auth,
+                                 const char *prog);
+
+extern maidag_script_fun script_handler;
+
+struct maidag_script
+{
+  maidag_script_fun fun;   /* Handler function */
+  const char *pat;         /* Script name pattern */
+};
+
+maidag_script_fun script_lang_handler (const char *lang);
+maidag_script_fun script_suffix_handler (const char *name);
+int script_register (const char *pattern);
+int script_apply (mu_message_t msg, struct mu_auth_data *auth);
+
+/* guile.c */
+extern int debug_guile;
+int scheme_check_msg (mu_message_t msg, struct mu_auth_data *auth,
+                     const char *prog);
+
+/* sieve.c */
+int sieve_check_msg (mu_message_t msg, struct mu_auth_data *auth,
+                    const char *prog);
+
diff --git a/maidag/script.c b/maidag/script.c
index c193a60..a5d5d89 100644
--- a/maidag/script.c
+++ b/maidag/script.c
@@ -1,6 +1,6 @@
 /* GNU Mailutils -- a suite of utilities for electronic mail
    Copyright (C) 1999, 2000, 2001, 2002, 2005,
-   2007 Free Software Foundation, Inc.
+   2007, 2009 Free Software Foundation, Inc.
 
    GNU Mailutils is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -19,129 +19,149 @@
 
 #include "maidag.h"
 
+struct script_tab
+{
+  char *lang;
+  char *suf;
+  maidag_script_fun fun;
+};
+
+struct script_tab script_tab[] = {
+  { "sieve",  "sv\0siv\0sieve\0", sieve_check_msg },
 #ifdef WITH_GUILE
-#include <mailutils/guile.h>
+  { "scheme", "scm\0", scheme_check_msg },
+#endif
+  { NULL }
+};
 
-int debug_guile;
+maidag_script_fun
+script_lang_handler (const char *lang)
+{
+  struct script_tab *p;
 
-SCM mda_catch_body (void *data, mu_mailbox_t mbox);
-SCM mda_catch_handler (void *unused, SCM tag, SCM throw_args);
-int mda_next (void *data, mu_mailbox_t mbox);
-int mda_exit (void *data, mu_mailbox_t mbox);
-int mda_init (void *data);
+  for (p = script_tab; p->lang; p++)
+    if (strcmp (p->lang, lang) == 0)
+      return p->fun;
+  return NULL;
+}
 
-int
-prog_mda (struct mda_data *data)
+maidag_script_fun
+script_suffix_handler (const char *name)
 {
-  char *x_argv[2];
-  mu_guimb_param_t param;
+  struct script_tab *p;
+  char *suf;
   
-  x_argv[0] = "maidag";
-  x_argv[1] = NULL;
-
-  param.debug_guile = debug_guile;
-  param.mbox = data->mbox;
-  param.user_name = NULL;
-  param.init = mda_init;
-  param.catch_body = mda_catch_body;
-  param.catch_handler = mda_catch_handler;
-  param.next = mda_next;
-  param.exit = mda_exit;
-  param.data = data;
+  suf = strrchr (name, '.');
+  if (!suf)
+    return NULL;
+  suf++;
   
-  mu_process_mailbox (1, x_argv, &param);
-  return EX_UNAVAILABLE;
-}
+  for (p = script_tab; p->lang; p++)
+    {
+      char *s;
 
-int
-mda_init (void *data)
-{
-  struct mda_data *md = data;
-  md->progfile = mu_expand_path_pattern (md->progfile_pattern, md->argv[0]);
-  return 0;
+      for (s = p->suf; *s; s += strlen (s) + 1)
+       if (strcmp (s, suf) == 0)
+         return p->fun;
+    }
+  return NULL;
 }
 
-static void
-mda_switch_to_user (struct mda_data *md)
+
+
+int
+script_register (const char *pattern)
 {
-  struct mu_auth_data *auth = NULL;
+  maidag_script_fun fun;
+  struct maidag_script *scr;
   
-  if (md && *md->argv != NULL)
-    auth = mu_get_auth_by_name (*md->argv);
-
-  if (auth)
+  if (script_handler)
+    fun = script_handler;
+  else
     {
-      switch_user_id (auth, 1);
-      chdir (auth->dir);
-      mu_auth_data_free (auth);
+      fun = script_suffix_handler (pattern);
+      if (!fun)
+       return EINVAL;
     }
-  else
+
+  scr = malloc (sizeof (*scr));
+  if (!scr)
+    return MU_ERR_FAILURE;
+  
+  scr->fun = fun;
+  scr->pat = pattern;
+
+  if (!script_list)
     {
-      switch_user_id (auth, 0);
-      chdir ("/");
+      if (mu_list_create (&script_list))
+       return MU_ERR_FAILURE;
     }
+
+  if (mu_list_append (script_list, scr))
+    return MU_ERR_FAILURE;
+
+  return 0;
 }
 
-SCM
-mda_catch_body (void *data, mu_mailbox_t mbox)
+
+struct apply_script_closure
 {
-  struct mda_data *md = data;
-  mu_message_t mesg = NULL;
-  mu_attribute_t attr = NULL;
+  struct mu_auth_data *auth;
+  mu_message_t msg;
+};
 
-  if (access (md->progfile, R_OK))
+static int
+apply_script (void *item, void *data)
+{
+  struct maidag_script *scr = item;
+  struct apply_script_closure *clos = data;
+  char *progfile;
+  int rc;
+  
+  progfile = mu_expand_path_pattern (scr->pat, clos->auth->name);
+  if (access (progfile, R_OK))
     {
       if (debug_level > 2)
-       mu_diag_output (MU_DIAG_DEBUG, _("Access to %s failed: %m"), 
md->progfile);
+       mu_diag_output (MU_DIAG_DEBUG, _("Access to %s failed: %m"), progfile);
+      free (progfile);
+      return 0;
     }
-  else
+
+  rc = scr->fun (clos->msg, clos->auth, progfile);
+  free (progfile);
+
+  if (rc == 0)
     {
-      mda_switch_to_user (md);
-      scm_primitive_load (scm_makfrom0str (md->progfile));
+      mu_attribute_t attr;
+      mu_message_get_attribute (clos->msg, &attr);
+      rc = mu_attribute_is_deleted (attr);
     }
   
-  mu_mailbox_get_message (mbox, 1, &mesg);
-  mu_message_get_attribute (mesg, &attr);
-  if (mu_attribute_is_deleted (attr))
-    return SCM_BOOL_F;
-
-  mda_switch_to_user (NULL);
-  mda (md->mbox, md->argv[0]);
-  return SCM_BOOL_F;
-}
-
-SCM
-mda_catch_handler (void *data, SCM tag, SCM throw_args)
-{
-  exit_code = EX_TEMPFAIL;
-  return scm_handle_by_message_noexit ("mail.local", tag, throw_args);
+  return rc;
 }
-
-int
-mda_next (void *data, mu_mailbox_t mbox)
-{
-  struct mda_data *md = data;
-  mu_message_t mesg = NULL;
-  mu_attribute_t attr = NULL;
-
-  md->argv++;
-  if (*md->argv == NULL)
-    return 0;
-  if (md->progfile)
-    free (md->progfile);
-  md->progfile = mu_expand_path_pattern (md->progfile_pattern, *md->argv);
-
-  mu_mailbox_get_message (mbox, 1, &mesg);
-  mu_message_get_attribute (mesg, &attr);
-  mu_attribute_unset_deleted (attr);
   
-  return md->progfile != NULL;
-}
-
 int
-mda_exit (void *data, mu_mailbox_t mbox)
+script_apply (mu_message_t msg, struct mu_auth_data *auth)
 {
-  return exit_code;
+  int rc = 0;
+  
+  if (script_list)
+    {
+      mu_attribute_t attr;
+      struct apply_script_closure clos;
+
+      clos.auth = auth;
+      clos.msg = msg;
+
+      mu_message_get_attribute (msg, &attr);
+      mu_attribute_unset_deleted (attr);
+      if (switch_user_id (auth, 1) == 0)
+       {
+         chdir (auth->dir);
+         rc = mu_list_do (script_list, apply_script, &clos);
+         chdir ("/");
+         switch_user_id (auth, 0);
+       }
+    }
+  return rc;
 }
-
-#endif
diff --git a/maidag/sieve.c b/maidag/sieve.c
new file mode 100644
index 0000000..92d2443
--- /dev/null
+++ b/maidag/sieve.c
@@ -0,0 +1,115 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+   Copyright (C) 1999, 2000, 2001, 2002, 2005,
+   2007, 2009 Free Software Foundation, Inc.
+
+   GNU Mailutils is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GNU Mailutils 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GNU Mailutils; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+   MA 02110-1301 USA */
+
+#include "maidag.h"
+
+static int
+_sieve_debug_printer (void *unused, const char *fmt, va_list ap)
+{
+  mu_diag_vprintf (MU_DIAG_DEBUG, fmt, ap);
+  return 0;
+}
+
+static void
+_sieve_action_log (void *user_name,
+                  const mu_sieve_locus_t *locus, size_t msgno,
+                  mu_message_t msg,
+                  const char *action, const char *fmt, va_list ap)
+{
+  int pfx = 0;
+  mu_debug_t debug;
+
+  mu_diag_get_debug (&debug);
+  mu_debug_set_locus (debug, locus->source_file, locus->source_line);
+  
+  mu_diag_printf (MU_DIAG_NOTICE, _("(user %s) "), (char*) user_name);
+  if (message_id_header)
+    {
+      mu_header_t hdr = NULL;
+      char *val = NULL;
+      mu_message_get_header (msg, &hdr);
+      if (mu_header_aget_value (hdr, message_id_header, &val) == 0
+         || mu_header_aget_value (hdr, MU_HEADER_MESSAGE_ID, &val) == 0)
+       {
+         pfx = 1;
+         mu_diag_printf (MU_DIAG_NOTICE, _("%s on msg %s"), action, val);
+         free (val);
+       }
+    }
+  
+  if (!pfx)
+    {
+      size_t uid = 0;
+      mu_message_get_uid (msg, &uid);
+      mu_diag_printf (MU_DIAG_NOTICE, _("%s on msg uid %d"), action, uid);
+    }
+  
+  if (fmt && strlen (fmt))
+    {
+      mu_diag_printf (MU_DIAG_NOTICE, "; ");
+      mu_diag_vprintf (MU_DIAG_NOTICE, fmt, ap);
+    }
+  mu_diag_printf (MU_DIAG_NOTICE, "\n");
+  mu_debug_set_locus (debug, NULL, 0);
+}
+
+static int
+_sieve_parse_error (void *user_name, const char *filename, int lineno,
+                   const char *fmt, va_list ap)
+{
+  mu_debug_t debug;
+
+  mu_diag_get_debug (&debug);
+  if (filename)
+    mu_debug_set_locus (debug, filename, lineno);
+
+  mu_diag_printf (MU_DIAG_ERROR, _("(user %s) "), (char*) user_name);
+  mu_diag_vprintf (MU_DIAG_ERROR, fmt, ap);
+  mu_diag_printf (MU_DIAG_ERROR, "\n");
+  mu_debug_set_locus (debug, NULL, 0);
+  return 0;
+}
+
+int
+sieve_check_msg (mu_message_t msg, struct mu_auth_data *auth, const char *prog)
+{
+  int rc;
+  mu_sieve_machine_t mach;
+
+  rc = mu_sieve_machine_init (&mach, auth->name);
+  if (rc)
+    {
+      mu_error (_("Cannot initialize sieve machine: %s"),
+               mu_strerror (rc));
+    }
+  else
+    {
+      mu_sieve_set_debug (mach, _sieve_debug_printer);
+      mu_sieve_set_debug_level (mach, sieve_debug_flags);
+      mu_sieve_set_parse_error (mach, _sieve_parse_error);
+      if (sieve_enable_log)
+       mu_sieve_set_logger (mach, _sieve_action_log);
+         
+      rc = mu_sieve_compile (mach, prog);
+      if (rc == 0)
+       mu_sieve_message (mach, msg);
+    }
+  return 0;
+}
+


hooks/post-receive
--
GNU Mailutils




reply via email to

[Prev in Thread] Current Thread [Next in Thread]