Commit 1c8dc53a 1c8dc53aa8d2e31174f1cfcb697551e2e49a6b98 by Sam Roberts

API of sieve engine now includes diagnostic callbacks, and is close to

stable, implementation will be soon.
1 parent 1d3fee57
/** mailutils' sieve implementation */
/*
Configuration:
debug level
print callback and callback pointer
ticket_t
"no actions" mode
Basic methods:
create and configure context
parse script
sieve message
Extended methods:
filter mailbox
filter mailbox URL
set ticket_t based on wicket file
*/
#include <errno.h>
#include <mailutils/message.h>
#include <mailutils/debug.h>
/* Sieve specific intepretations of error numbers. */
#define SV_EPARSE ENOEXEC
extern const char* sv_strerror(int e);
typedef struct sv_interp_ctx_t* sv_interp_t;
typedef struct sv_script_ctx_t* sv_script_t;
typedef void (*sv_parse_error_t)(const char* script, int lineno, const char* errmsg);
typedef void (*sv_execute_error_t)(const char* script, message_t msg, int rc, const char* errmsg);
typedef void (*sv_action_log_t)(const char* script, message_t msg, const char* action, const char* fmt, va_list ap);
extern int sv_interp_alloc(sv_interp_t *i,
sv_parse_error_t pe,
sv_execute_error_t ee,
sv_action_log_t al);
extern void sv_interp_free(sv_interp_t *i);
extern int sv_script_parse(sv_script_t *s, sv_interp_t i, const char *script);
extern void sv_script_free(sv_script_t *s);
#define SV_FLAG_NO_ACTIONS 0x01
/* Sieve specific debug levels - can be set in the mu_debug_t. */
#define SV_DEBUG_TRACE 0x100
#define SV_DEBUG_HDR_FILL 0x200
#define SV_DEBUG_MSG_QUERY 0x400
extern int sv_script_execute(sv_script_t s, message_t m, ticket_t t, mu_debug_t d, int svflags);
......@@ -14,11 +14,15 @@ sv_getsize (void *mc, int *size)
sv_msg_ctx_t *m = (sv_msg_ctx_t *) mc;
size_t sz = 0;
message_size (m->msg, &sz);
m->rc = message_size (m->msg, &sz);
if(m->rc)
return SIEVE_RUN_ERROR;
*size = sz;
sv_print (m->ic, SV_PRN_QRY, "getsize -> %d\n", *size);
mu_debug_print (m->debug, SV_DEBUG_MSG_QUERY, "msg uid %d getsize: %d\n",
m->uid, size);
return SIEVE_OK;
}
......@@ -48,7 +52,9 @@ sv_getheader (void *mc, const char *name, const char ***body)
header_get_field_count (h, &i);
sv_print (m->ic, SV_PRN_QRY, "getheader, filling cache with %d fields\n", i);
mu_debug_print (m->debug, SV_DEBUG_HDR_FILL,
"msg uid %d cache: filling with %d fields\n",
m->uid, i);
for (; i > 0; i--)
{
......@@ -59,7 +65,9 @@ sv_getheader (void *mc, const char *name, const char ***body)
if (m->rc)
break;
sv_print (m->ic, SV_PRN_QRY, "getheader, cacheing %s=%s\n", fn, fv);
mu_debug_print (m->debug, SV_DEBUG_HDR_FILL,
"msg uid %d cache: %s: %s\n", m->uid,
fn, fv);
m->rc = sv_field_cache_add (&m->cache, fn, fv);
......@@ -67,7 +75,7 @@ sv_getheader (void *mc, const char *name, const char ***body)
{
fv = 0; /* owned by the cache */
}
if (m->rc)
else
break;
/* the cache doesn't want it, and we don't need it */
......@@ -76,28 +84,39 @@ sv_getheader (void *mc, const char *name, const char ***body)
}
free (fn);
free (fv);
if (m->rc)
{
mu_debug_print (m->debug, SV_DEBUG_MSG_QUERY,
"msg uid %d cache: failed %s\n",
m->uid, sv_strerror (m->rc));
if (m->rc == ENOMEM)
return SIEVE_NOMEM;
return SIEVE_RUN_ERROR;
}
}
if (!m->rc)
{
m->rc = sv_field_cache_get (&m->cache, name, body);
}
if (m->rc)
{
sv_print (m->ic, SV_PRN_QRY, "getheader %s, failed %s\n", name, strerror (m->rc));
}
else
if ((m->rc = sv_field_cache_get (&m->cache, name, body)) == EOK)
{
const char **b = *body;
int i = 1;
sv_print (m->ic, SV_PRN_QRY, "getheader, %s=%s", name, b[0]);
while (b[0] && b[i])
int i;
for (i = 0; b[i]; i++)
{
sv_print (m->ic, SV_PRN_QRY, ", %s", b[i]);
i++;
mu_debug_print (m->debug, SV_DEBUG_MSG_QUERY, "msg uid %d getheader: %s: %s\n",
m->uid, name, b[i]);
}
sv_print (m->ic, SV_PRN_QRY, "\n");
}
return sv_mu_errno_to_rc (m->rc);
else
mu_debug_print (m->debug, SV_DEBUG_MSG_QUERY, "msg uid %d getheader: %s (not found)\n",
m->uid, name);
switch (m->rc)
{
case 0:
return SIEVE_OK;
case ENOMEM:
return SIEVE_NOMEM;
}
return SIEVE_RUN_ERROR;
}
/*
......@@ -136,21 +155,30 @@ void* mc, // from sieve_execute_script(, mc);
const char** errmsg // fill it in if you return failure
*/
void
sv_print_action (const char* a, void *ac, void *ic, void *sc, void *mc)
static void action_log(sv_msg_ctx_t* mc, const char* action, const char* fmt, ...)
{
//sv_msg_ctx_t *m = (sv_msg_ctx_t *) mc;
const char* script = mc->sc->file;
message_t msg = mc->msg;
sv_action_log_t log = mc->sc->ic->action_log;
va_list ap;
va_start(ap, fmt);
sv_print (ic, SV_PRN_ACT, "action => %s\n", a);
if(log)
log(script, msg, action, fmt, ap);
va_end(ap);
}
int
sv_keep (void *ac, void *ic, void *sc, void *mc, const char **errmsg)
{
//sv_msg_ctx_t * m = (sv_msg_ctx_t *) mc;
sv_msg_ctx_t * m = (sv_msg_ctx_t *) mc;
//sieve_keep_context_t * a = (sieve_keep_context_t *) ac;
*errmsg = "keep";
m->rc = 0;
sv_print_action ("KEEP", ac, ic, sc, mc);
action_log (mc, "KEEP", "");
return SIEVE_OK;
}
......@@ -160,75 +188,78 @@ sv_fileinto (void *ac, void *ic, void *sc, void *mc, const char **errmsg)
{
sieve_fileinto_context_t *a = (sieve_fileinto_context_t *) ac;
sv_msg_ctx_t *m = (sv_msg_ctx_t *) mc;
sv_interp_ctx_t *i = (sv_interp_ctx_t *) ic;
const char *what = "fileinto";
int res = 0;
sv_print_action ("FILEINTO", ac, ic, sc, mc);
sv_print (i, SV_PRN_ACT, " into <%s>\n", a->mailbox);
m->rc = 0;
if (!i->opt_no_actions)
{
res = sv_mu_save_to (a->mailbox, m->msg, i->ticket, &what);
}
action_log (mc, "FILEINTO", "save copy in %s", a->mailbox);
if (res)
if ((m->svflags & SV_FLAG_NO_ACTIONS) == 0)
{
assert(what);
*errmsg = strerror (res);
sv_print (i, SV_PRN_ACT, " %s failed with [%d] %s\n",
what, res, *errmsg);
*errmsg = "fileinto(saving)";
m->rc = sv_mu_save_to (a->mailbox, m->msg, m->ticket, m->debug);
if (!m->rc)
{
*errmsg = "fileinto(saving)";
m->rc = sv_mu_mark_deleted (m->msg);
}
}
m->rc = res;
return res ? SIEVE_FAIL : SIEVE_OK;
return m->rc ? SIEVE_FAIL : SIEVE_OK;
}
int
sv_redirect (void *ac, void *ic, void *sc, void *mc, const char **errmsg)
{
sv_print_action ("REDIRECT", ac, ic, sc, mc);
sv_msg_ctx_t *m = (sv_msg_ctx_t *) mc;
return SIEVE_OK;
*errmsg = "redirect";
m->rc = 0;
action_log (mc, "REDIRECT", "");
return SIEVE_FAIL;
}
int
sv_discard (void *ac, void *ic, void *sc, void *mc, const char **errmsg)
{
sv_msg_ctx_t *m = (sv_msg_ctx_t *) mc;
sv_interp_ctx_t *i = (sv_interp_ctx_t *) ic;
int res = 0;
//sv_interp_ctx_t *i = (sv_interp_ctx_t *) ic;
*errmsg = "discard";
m->rc = 0;
sv_print_action ("DISCARD", ac, ic, sc, mc);
action_log(mc, "DISCARD", "");
if (!i->opt_no_actions)
if ((m->svflags & SV_FLAG_NO_ACTIONS) == 0)
{
res = sv_mu_mark_deleted (m->msg);
m->rc = sv_mu_mark_deleted (m->msg);
}
if (res)
*errmsg = strerror (res);
return SIEVE_OK;
return m->rc ? SIEVE_FAIL : SIEVE_OK;
}
int
sv_reject (void *ac, void *ic, void *sc, void *mc, const char **errmsg)
{
sv_print_action ("REJECT", ac, ic, sc, mc);
sv_msg_ctx_t *m = (sv_msg_ctx_t *) mc;
return SIEVE_OK;
*errmsg = "reject";
m->rc = 0;
action_log(mc, "REJECT", "");
return SIEVE_FAIL;
}
/*
#if 0
int sv_notify(void *ac, void *ic, void *sc, void *mc, const char **errmsg)
{
sv_print_action("NOTIFY", ac, ic, sc, mc);
return SIEVE_OK;
}
*/
#endif
int
sv_autorespond (void *ac, void *ic, void *sc, void *mc, const char **errmsg)
......@@ -259,9 +290,11 @@ sieve_imapflags_t mark = { markflags, 1 };
int
sv_parse_error (int lineno, const char *msg, void *ic, void *sc)
{
sv_interp_ctx_t* i = (sv_interp_ctx_t*) ic;
sv_interp_ctx_t *i = (sv_interp_ctx_t *) ic;
sv_script_ctx_t *s = (sv_script_ctx_t *) sc;
sv_print (i, SV_PRN_ERR, "%s:%d: %s\n", i->opt_script, lineno, msg);
if (i->parse_error)
i->parse_error (s->file, lineno, msg);
return SIEVE_OK;
}
......@@ -269,122 +302,61 @@ sv_parse_error (int lineno, const char *msg, void *ic, void *sc)
int
sv_execute_error (const char *msg, void *ic, void *sc, void *mc)
{
sv_interp_ctx_t* i = (sv_interp_ctx_t*) ic;
sv_print (i, SV_PRN_ERR, "sieve execute failed, %s\n", msg);
return SIEVE_OK;
}
int
sv_summary (const char *msg, void *ic, void *sc, void *mc)
{
sv_msg_ctx_t *m = (sv_msg_ctx_t *) mc;
sv_interp_ctx_t *i = (sv_interp_ctx_t *) ic;
sv_script_ctx_t *s = (sv_script_ctx_t *) sc;
sv_msg_ctx_t *m = (sv_msg_ctx_t *) sc;
m->summary = strdup (msg);
if (i->execute_error)
i->execute_error (s->file, m->msg, m->rc, msg);
return SIEVE_OK;
}
/* register all these callbacks */
/* register supported callbacks */
int
sv_register_callbacks (sieve_interp_t * i)
{
int res;
int rc = 0;
/* These 4 callbacks are mandatory. */
if (rc == EOK)
rc = sieve_register_size (i, &sv_getsize);
if (rc == EOK)
rc = sieve_register_header (i, &sv_getheader);
if (rc == EOK)
rc = sieve_register_redirect (i, &sv_redirect);
if (rc == EOK)
rc = sieve_register_keep (i, &sv_keep);
res = sieve_register_size (i, &sv_getsize);
if (res != SIEVE_OK)
{
printf ("sieve_register_size() returns %d\n", res);
exit (1);
}
res = sieve_register_header (i, &sv_getheader);
if (res != SIEVE_OK)
{
printf ("sieve_register_header() returns %d\n", res);
exit (1);
}
res = sieve_register_redirect (i, &sv_redirect);
if (res != SIEVE_OK)
{
printf ("sieve_register_redirect() returns %d\n", res);
exit (1);
}
res = sieve_register_keep (i, &sv_keep);
if (res != SIEVE_OK)
{
printf ("sieve_register_keep() returns %d\n", res);
exit (1);
}
#if 0
res = sieve_register_envelope (i, &sv_getenvelope);
if (res != SIEVE_OK)
{
printf ("sieve_register_envelope() returns %d\n", res);
exit (1);
}
if (rc == EOK)
rc = sieve_register_envelope (i, &sv_getenvelope);
#endif
res = sieve_register_discard (i, &sv_discard);
if (res != SIEVE_OK)
{
printf ("sieve_register_discard() returns %d\n", res);
exit (1);
}
res = sieve_register_reject (i, &sv_reject);
if (res != SIEVE_OK)
{
printf ("sieve_register_reject() returns %d\n", res);
exit (1);
}
res = sieve_register_fileinto (i, &sv_fileinto);
if (res != SIEVE_OK)
{
printf ("sieve_register_fileinto() returns %d\n", res);
exit (1);
}
if (rc == EOK)
rc = sieve_register_discard (i, &sv_discard);
#if 0
res = sieve_register_vacation (i, &sv_vacation);
if (res != SIEVE_OK)
{
printf ("sieve_register_vacation() returns %d\n", res);
exit (1);
}
res = sieve_register_imapflags (i, &mark);
if (res != SIEVE_OK)
{
printf ("sieve_register_imapflags() returns %d\n", res);
exit (1);
}
if (rc == EOK)
rc = sieve_register_reject (i, &sv_reject);
#endif
/* The "fileinto" extension. */
if (rc == EOK)
rc = sieve_register_fileinto (i, &sv_fileinto);
#if 0
res = sieve_register_notify (i, &sv_notify);
if (res != SIEVE_OK)
{
printf ("sieve_register_notify() returns %d\n", res);
exit (1);
}
if (rc == EOK)
rc = sieve_register_vacation (i, &sv_vacation);
#endif
res = sieve_register_parse_error (i, &sv_parse_error);
if (res != SIEVE_OK)
{
printf ("sieve_register_parse_error() returns %d\n", res);
exit (1);
}
res = sieve_register_execute_error (i, &sv_execute_error);
if (res != SIEVE_OK)
{
printf ("sieve_register_execute_error() returns %d\n", res);
exit (1);
}
res = sieve_register_summary (i, &sv_summary);
if (res != SIEVE_OK)
{
printf ("sieve_register_summary() returns %d\n", res);
exit (1);
}
return res;
#if 0
if (rc == EOK)
rc = sieve_register_imapflags (i, &mark);
#endif
#if 0
if (rc == EOK)
rc = sieve_register_notify (i, &sv_notify);
#endif
if (rc == EOK)
rc = sieve_register_parse_error (i, &sv_parse_error);
if (rc == EOK)
rc = sieve_register_execute_error (i, &sv_execute_error);
return rc;
}
......
#include "sv.h"
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
const char *
sv_strerror (int e)
{
switch (e)
{
case SV_EPARSE:
return "parse failure";
}
return strerror (e);
}
int
sv_interp_alloc (sv_interp_t * ip,
sv_parse_error_t pe,
sv_execute_error_t ee, sv_action_log_t al)
{
int rc = 0;
sv_interp_t i = calloc (1, sizeof (struct sv_interp_ctx_t));
if (!i)
return ENOMEM;
i->parse_error = pe;
i->execute_error = ee;
i->action_log = al;
if (sieve_interp_alloc (&i->interp, i) != SIEVE_OK)
{
free (i);
return ENOMEM;
}
if ((rc = sv_register_callbacks (i->interp)))
{
sieve_interp_free (&i->interp);
free (i);
return rc;
}
*ip = i;
return EOK;
}
void
sv_interp_free (sv_interp_t * ip)
{
sv_interp_t i = *ip;
if (i)
{
if (i->interp)
sieve_interp_free (&i->interp);
free (i);
}
*ip = 0;
}
int
sv_script_parse (sv_script_t * sp, sv_interp_t i, const char *script)
{
FILE *f = 0;
sv_script_t s = 0;
int rc = 0;
if (!i || !script)
return EINVAL;
if ((s = calloc (1, sizeof (struct sv_script_ctx_t))) == 0)
return ENOMEM;
s->ic = i;
if ((s->file = strdup (script)) == 0)
{
free (s);
return ENOMEM;
}
f = fopen (script, "r");
if (!f)
{
free (s->file);
free (s);
return errno;
}
rc = sieve_script_parse (i->interp, f, s, &s->script);
fclose (f);
switch (rc)
{
case SIEVE_NOT_FINALIZED:
free (s->file);
free (s);
return EINVAL;
case SIEVE_PARSE_ERROR:
free (s->file);
free (s);
return ENOEXEC;
case SIEVE_NOMEM:
free (s->file);
free (s);
return ENOMEM;
}
*sp = s;
return 0;
}
void
sv_script_free (sv_script_t * sp)
{
sv_script_t s = *sp;
if (s)
{
if (s->script)
sieve_script_free (&s->script);
free (s->file);
free (s);
}
*sp = 0;
}
int sv_script_execute (sv_script_t s, message_t m, ticket_t t, mu_debug_t d,
int svflags)
{
sv_msg_ctx_t mc = { 0, };
int rc;
mc.sc = s;
mc.msg = m;
mc.ticket = t;
mc.debug = d;
mc.svflags = svflags;
message_get_uid (m, &mc.uid);
rc = sieve_execute_script (s->script, &mc);
sv_field_cache_release (&mc.cache);
if(rc && mc.rc)
rc = mc.rc;
return rc;
}
......
......@@ -75,15 +75,23 @@ sv_field_cache_add (sv_field_cache_t * m, const char *name, char *body)
}
else
{
char* n = 0;
/* create a new entry in the hash table */
sv_field_t* field = (sv_field_t *) malloc (sizeof (sv_field_t) +
8 * sizeof (char *));
if (field)
{
return ENOMEM;
}
if (!field)
return ENOMEM;
n = strdup (name);
if(!n)
{
free(field);
return ENOMEM;
}
m->cache[cl] = field;
m->cache[cl]->name = strdup (name);
m->cache[cl]->name = n;
m->cache[cl]->contents[0] = body;
m->cache[cl]->ncontents = 1;
}
......
/** utility wrappers around mailutils functionality **/
#include <errno.h>
#include <string.h>
#include "sv.h"
......@@ -13,7 +14,7 @@ sv_mu_errno_to_rc (int eno)
return SIEVE_NOMEM;
case ENOENT:
return SIEVE_FAIL;
case EOK:
case 0:
return SIEVE_OK;
}
return SIEVE_INTERNAL_ERROR;
......@@ -21,13 +22,6 @@ sv_mu_errno_to_rc (int eno)
/* we hook mailutils debug output into our diagnostics using this */
int
sv_mu_debug_print (mu_debug_t d, const char *fmt, va_list ap)
{
sv_printv(mu_debug_get_owner(d), SV_PRN_MU, fmt, ap);
return 0;
}
int
sv_mu_copy_debug_level (const mailbox_t from, mailbox_t to)
......@@ -54,85 +48,62 @@ sv_mu_copy_debug_level (const mailbox_t from, mailbox_t to)
}
int
sv_mu_save_to (const char *toname, message_t mesg,
ticket_t ticket, const char **errmsg)
sv_mu_save_to (const char *toname, message_t msg,
ticket_t ticket, mu_debug_t debug)
{
int res = 0;
int rc = 0;
mailbox_t to = 0;
mailbox_t from = 0;
res = mailbox_create_default (&to, toname);
rc = mailbox_create_default (&to, toname);
if (res == ENOENT)
*errmsg = "no handler for this type of mailbox";
if (!rc && debug)
{
mailbox_set_debug (to, debug);
}
if (!res && ticket)
if (!rc && ticket)
{
folder_t folder = NULL;
authority_t auth = NULL;
if (!res)
{
*errmsg = "mailbox_get_folder";
res = mailbox_get_folder (to, &folder);
}
if (!res)
{
*errmsg = "folder_get_authority";
res = folder_get_authority (folder, &auth);
}
if (!res)
{
*errmsg = "authority_set_ticket";
res = authority_set_ticket (auth, ticket);
}
}
if (!res)
{
if (message_get_mailbox (mesg, &from) == 0)
sv_mu_copy_debug_level (from, to);
}
if (!res)
{
*errmsg = "mailbox_open";
res = mailbox_open (to, MU_STREAM_WRITE | MU_STREAM_CREAT);
if (!rc)
rc = mailbox_get_folder (to, &folder);
if (!rc)
rc = folder_get_authority (folder, &auth);
if (!rc)
rc = authority_set_ticket (auth, ticket);
}
if (!res)
if (!rc)
rc = mailbox_open (to, MU_STREAM_WRITE | MU_STREAM_CREAT);
if (!rc)
rc = mailbox_append_message (to, msg);
if (!rc)
rc = mailbox_close (to);
else
{
*errmsg = "mailbox_append_message";
res = mailbox_append_message (to, mesg);
if (!res)
{
*errmsg = "mailbox_close";
res = mailbox_close (to);
}
else
{
mailbox_close (to);
}
mailbox_close (to);
}
mailbox_destroy (&to);
if(res == 0)
*errmsg = 0;
mailbox_destroy (&to);
return res;
return rc;
}
int
sv_mu_mark_deleted (message_t msg)
{
attribute_t attr = 0;
int res;
int rc;
res = message_get_attribute (msg, &attr);
rc = message_get_attribute (msg, &attr);
if (!res)
if (!rc)
attribute_set_deleted (attr);
return res;
return rc;
}
......