Commit 43088a1c 43088a1c350b70b627edeb98d927beca3bcd7708 by Sergey Poznyakoff

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.
1 parent 303823bd
/* 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;
}
......
/* 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);
......
......@@ -146,18 +146,20 @@ static const char *guimb_argp_capa[] = {
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;
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;
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 ();
return !!rc;
}
char *
......
......@@ -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)
......
#! %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,11 +932,15 @@
outfile
(lambda ()
(display "#! ")
(if guimb-header
(display "/home/gray/alpha/bin/guimb -s\n")
(display "/usr/bin/guile -s\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
"# Guile mailbox parser made from " filename))
"# This Guile mailbox parser was made from " filename))
(newline)
(display "# by sieve.scm, GNU %PACKAGE% %VERSION%\n!#")
(newline)
......@@ -958,12 +963,22 @@
(newline)
(if request-verbose
(display "(set! sieve-verbose #t)\n"))
(display "(sieve-main ")
(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")
(sieve-code-print-list
(append '(lambda ())
sieve-code-list))
(display ")"))))
(display "\n\
;;;; Local Variables:\n\
;;;; buffer-read-only: t\n\
;;;; End:\n"))))
;;;;
......
......@@ -69,9 +69,19 @@ 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
}
......
......@@ -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\
......
/* 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;
}
......@@ -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);
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)
{
......
......@@ -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);
......
......@@ -49,4 +49,3 @@
#include <mailutils/guile.h>
......
......@@ -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
......
......@@ -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;
......@@ -323,7 +310,7 @@ 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);
......
/* 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"
#ifdef WITH_GUILE
#include <mailutils/guile.h>
int debug_guile;
static int initialized;
int
scheme_check_msg (mu_message_t msg, struct mu_auth_data *auth,
const char *prog)
{
if (!initialized)
{
mu_guile_init (debug_guile);
initialized = 1;
}
mu_guile_load (prog, 0, NULL);
mu_guile_message_apply (msg, "mailutils-check-message");
return 0;
}
#endif
......@@ -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;
}
sender_address = arg;
break;
#ifdef WITH_GUILE
case 'l':
script_handler = script_lang_handler (arg);
if (!script_handler)
argp_error (state, _("Unknown or unsupported language: %s"),
arg);
break;
case 's':
mu_argp_node_list_new (&lst, "guile-filter", arg);
switch (script_register (arg))
{
case 0:
break;
#endif
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
{
mu_sieve_machine_t mach;
rc = mu_sieve_machine_init (&mach, auth->name);
if (rc)
struct mu_cfg_section *section;
if (mu_create_canned_section ("filter", &section) == 0)
{
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);
......
......@@ -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);
......
/* 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"
#ifdef WITH_GUILE
#include <mailutils/guile.h>
int debug_guile;
struct script_tab
{
char *lang;
char *suf;
maidag_script_fun fun;
};
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);
struct script_tab script_tab[] = {
{ "sieve", "sv\0siv\0sieve\0", sieve_check_msg },
#ifdef WITH_GUILE
{ "scheme", "scm\0", scheme_check_msg },
#endif
{ NULL }
};
int
prog_mda (struct mda_data *data)
maidag_script_fun
script_lang_handler (const char *lang)
{
char *x_argv[2];
mu_guimb_param_t param;
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;
mu_process_mailbox (1, x_argv, &param);
return EX_UNAVAILABLE;
}
struct script_tab *p;
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 (p = script_tab; p->lang; p++)
if (strcmp (p->lang, lang) == 0)
return p->fun;
return NULL;
}
static void
mda_switch_to_user (struct mda_data *md)
maidag_script_fun
script_suffix_handler (const char *name)
{
struct mu_auth_data *auth = NULL;
struct script_tab *p;
char *suf;
if (md && *md->argv != NULL)
auth = mu_get_auth_by_name (*md->argv);
suf = strrchr (name, '.');
if (!suf)
return NULL;
suf++;
if (auth)
for (p = script_tab; p->lang; p++)
{
switch_user_id (auth, 1);
chdir (auth->dir);
mu_auth_data_free (auth);
}
else
{
switch_user_id (auth, 0);
chdir ("/");
char *s;
for (s = p->suf; *s; s += strlen (s) + 1)
if (strcmp (s, suf) == 0)
return p->fun;
}
return NULL;
}
SCM
mda_catch_body (void *data, mu_mailbox_t mbox)
int
script_register (const char *pattern)
{
struct mda_data *md = data;
mu_message_t mesg = NULL;
mu_attribute_t attr = NULL;
maidag_script_fun fun;
struct maidag_script *scr;
if (access (md->progfile, R_OK))
if (script_handler)
fun = script_handler;
else
{
if (debug_level > 2)
mu_diag_output (MU_DIAG_DEBUG, _("Access to %s failed: %m"), md->progfile);
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)
{
mda_switch_to_user (md);
scm_primitive_load (scm_makfrom0str (md->progfile));
if (mu_list_create (&script_list))
return MU_ERR_FAILURE;
}
mu_mailbox_get_message (mbox, 1, &mesg);
mu_message_get_attribute (mesg, &attr);
if (mu_attribute_is_deleted (attr))
return SCM_BOOL_F;
if (mu_list_append (script_list, scr))
return MU_ERR_FAILURE;
mda_switch_to_user (NULL);
mda (md->mbox, md->argv[0]);
return SCM_BOOL_F;
return 0;
}
SCM
mda_catch_handler (void *data, SCM tag, SCM throw_args)
struct apply_script_closure
{
exit_code = EX_TEMPFAIL;
return scm_handle_by_message_noexit ("mail.local", tag, throw_args);
}
struct mu_auth_data *auth;
mu_message_t msg;
};
int
mda_next (void *data, mu_mailbox_t mbox)
static int
apply_script (void *item, void *data)
{
struct mda_data *md = data;
mu_message_t mesg = NULL;
mu_attribute_t attr = NULL;
struct maidag_script *scr = item;
struct apply_script_closure *clos = data;
char *progfile;
int rc;
md->argv++;
if (*md->argv == NULL)
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"), progfile);
free (progfile);
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);
rc = scr->fun (clos->msg, clos->auth, progfile);
free (progfile);
if (rc == 0)
{
mu_attribute_t attr;
mu_message_get_attribute (clos->msg, &attr);
rc = mu_attribute_is_deleted (attr);
}
return md->progfile != NULL;
return rc;
}
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;
#endif
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;
}
......
/* 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;
}