This code hasn't really been tested, but it reads well and compiles :-)
Now supported is an extended version of Jonas' regexes and sieve actions.
Note that those functions aren't actually included here. Rather, this is
merely the delivery chain that enables those to be used whenever ready.
By commenting out lines 69 and 77, this file should link into an existing
dbmail and continue operating normally. Uhh, hypothetically that is ;-)
Next up: editing pipe.c and forward.c with some minor back changes.
After that: rewrite Jonas' Regex code to match the filters.c consumer.
Aaron
/* $Id: filters.c,v 1.0 2003/04/29 16:04:08 aaron Exp $
* (c) 2003 Aaron Stone
*
* Functions for running user defined filters
* on a message in the temporary store, usually
* just delivering the message to the user's INBOX
* ...unless they have fancy filters defined, that is :-)
* */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <time.h>
#include <ctype.h>
#include "db.h"
#include "auth.h"
#include "debug.h"
#include "list.h"
#include "bounce.h"
#include "forward.h"
#include "filters.h"
#include "dbmail.h"
#include "debug.h"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include "dbmd5.h"
#include "misc.h"
extern struct list smtpItems, sysItems;
/* Run the user's filters on this message
* Retrieve the action list as either
* a linked list of things to do, or a
* single thing to do. Not sure yet...
*
* Then do it!
* */
int filter_deliver(u64_t msgidnr, char *header, u64_t headersize, u64_t
useridnr, char *mailbox)
{
field_t val;
int do_regex = 0, do_sieve = 0;
struct list actions;
struct element *tmp;
int actiontaken = 0, ret = 0;
u64_t mboxidnr, newmsgidnr;
u64_t totalmem;
char *unique_id = NULL, *bounce_id = NULL;
GetConfigValue("SQLREGEX", &smtpItems, val);
if (strcasecmp(val, "yes") == 0)
do_regex = 1;
GetConfigValue("LIBSIEVE", &smtpItems, val);
if (strcasecmp(val, "yes") == 0)
do_sieve = 1;
list_init(&actions);
if (do_regex)
{
/* Call out to Jonas' regex filter function!
* */
ret = db_regexfilter(useridnr, header, actions);
}
if (do_sieve)
{
/* Don't code the Sieve guts right here,
* call out to a function that encapsulates it!
* */
ret = db_sievefilter(useridnr, NULL, header, actions);
}
if (mailbox == NULL)
{
mailbox = (char *)my_malloc(strlen("INBOX")+1);
snprintf(mailbox, strlen("INBOX")+1, "INBOX");
}
/* actions is a list of things to do with this message
* each data pointer in the actions list references
* a structure like this:
*
* typedef filter_action {
* int method,
* char *destination,
* char *message
* } filter_action_t;
*
* Where message is some descriptive text, used
* primarily for rejection noticed, and where
* destination is either a mailbox name or a
* forwarding address, and method is one of these:
*
* FA_KEEP,
* FA_DISCARD,
* FA_REDIRECT,
* FA_REJECT,
* FA_FILEINTO
* (see RFC 3028 [SIEVE] for details)
*
* FA_SIEVE:
* In addition, this implementation allows for
* the internel Regex matching to call a Sieve
* script into action. In this case, the method
* is FA_SIEVE and the destination is the script's name.
* Note that Sieve must be enabled in the configuration
* file or else an error will be generated.
*
* In the absence of any valid actions (ie. actions
* is an empty list, or all attempts at performing the
* actions fail...) an implicit FA_KEEP is performed,
* using INBOX as the destination (hardcoded).
* */
if (list_totalnodes(&actions) > 0)
{
tmp=list_getstart(&actions);
while (tmp != NULL)
{
/* Try not to think about the structures too hard ;-) */
switch ( (int)((filter_action_t *)tmp->data)->method )
{
case FA_SIEVE:
{
/* Run the script specified by destination and
* add the resulting list onto the *end* of the
* actions list. Note that this is a deep hack...
* */
if ((char *)((filter_action_t *)tmp->data)->destination !=
NULL)
{
struct list localtmplist;
struct element *localtmpelem;
if (db_sievefilter(useridnr, (char *)((filter_action_t
*)tmp->data)->destination, header, localtmplist))
{
/* Fast forward to the end of the actions list */
localtmpelem=list_getstart(&actions);
while (localtmpelem != NULL)
{
localtmpelem=localtmpelem->nextnode;
}
/* And tack on the start of the Sieve list */
localtmpelem->nextnode = list_getstart(&localtmplist);
/* Remeber to increment the node count, too */
actions.total_nodes += list_totalnodes(&localtmplist);
}
}
break;
}
case FA_FILEINTO:
{
/* If the action doesn't come with a mailbox, use the
default. */
if ((char *)((filter_action_t *)tmp->data)->destination ==
NULL)
{
mboxidnr = db_findmailbox(mailbox, useridnr);
}
else
{
mboxidnr = db_findmailbox((char *)((filter_action_t
*)tmp->data)->destination, useridnr);
}
/* Did we fail to find the mailbox? */
if (mboxidnr == 0 || mboxidnr == (u64_t)(-1))
{
/* See which one has a box name to create! */
if ((char *)((filter_action_t *)tmp->data)->destination
== NULL)
{
mboxidnr = db_createmailbox(mailbox, useridnr);
}
else
{
mboxidnr = db_createmailbox((char *)((filter_action_t
*)tmp->data)->destination, useridnr);
}
}
/* Did we fail to create the mailbox? */
if (mboxidnr == 0 || mboxidnr == (u64_t)(-1))
{
/* Serious failure situation! */
}
else
{
newmsgidnr = db_copymsg(msgidnr, mboxidnr);
switch (newmsgidnr)
{
case -2:
bounce_id = auth_get_userid(&useridnr);
bounce (header, headersize, bounce_id,
BOUNCE_STORAGE_LIMIT_REACHED);
my_free (bounce_id);
break;
case -1:
trace(TRACE_ERROR, "insert_messages(): error
copying message to user [%llu]", useridnr);
/* Don't worry about error conditions.
* It's annoying if the message isn't delivered,
* but as long as *something* happens it's OK.
* Otherwise, actiontaken will be 0 and another
* delivery attempt will be made before passing
* up the error at the end of the function.
* */
break;
default:
trace (TRACE_MESSAGE,"insert_messages(): message
id=%llu, size=%llu is inserted",
msgidnr, totalmem+headersize);
/* Create a unique ID for this message;
* Each message for each user must have a unique
ID!
* */
create_unique_id (unique_id, msgidnr);
db_update_message (msgidnr, unique_id,
totalmem+headersize, 0);
actiontaken = 1;
break;
}
}
break;
}
case FA_DISCARD:
{
/* Basically do nothing! */
actiontaken = 1;
break;
}
case FA_REJECT:
{
bounce_id = auth_get_userid(&useridnr);
// FIXME: I'm happy with this code, but it's not quite
right...
// Plus we want to specify a message to go along with it!
bounce (header, headersize, bounce_id, BOUNCE_NO_SUCH_USER);
my_free (bounce_id);
actiontaken = 1;
break;
}
case FA_REDIRECT:
{
char *forward_id;
struct list targets;
list_nodeadd(&targets, (char *)((filter_action_t
*)tmp->data)->destination, strlen((char *)((filter_action_t
*)tmp->data)->destination));
/* Put the destination into the targets list */
/* The From header will contain... */
forward_id = auth_get_userid(&useridnr);
forward (msgidnr, &targets, forward_id, header, headersize);
list_freelist(&targets.start);
my_free (forward_id);
actiontaken = 1;
break;
}
/*
case FA_KEEP:
default:
{
// Don't worry! This is handled by implicit keep :-)
break;
}
*/
}
tmp=tmp->nextnode;
}
}
else
{
/* Might as well be explicit about this... */
actiontaken = 0;
}
if (actiontaken == 0)
{
/* This is that implicit keep I mentioned earlier...
* If possible, put the message in the specified
* mailbox, otherwise use INBOX. */
mboxidnr = db_findmailbox(mailbox, useridnr);
/* Did we fail to find the mailbox? */
if (mboxidnr == 0 || mboxidnr == (u64_t)(-1))
{
mboxidnr = db_createmailbox(mailbox, useridnr);
/* Did we fail to create the mailbox? */
if (mboxidnr == 0 || mboxidnr == (u64_t)(-1))
{
/* Serious failure situation! */
}
else
{
newmsgidnr = db_copymsg(msgidnr, mboxidnr);
switch (newmsgidnr)
{
case -2:
bounce_id = auth_get_userid(&useridnr);
bounce (header, headersize, bounce_id,
BOUNCE_STORAGE_LIMIT_REACHED);
my_free (bounce_id);
break;
case -1:
trace(TRACE_ERROR, "insert_messages(): error copying
message to user [%llu]", useridnr);
/* FIXME: We need a way to pass this error back out
* so that, for example, the LMTP server can tell the
* MTA that it wasn't able to deliver successfully! */
break;
default:
trace (TRACE_MESSAGE,"insert_messages(): message id=%llu,
size=%llu is inserted",
msgidnr, totalmem+headersize);
/* Create a unique ID for this message;
* Each message for each user must have a unique ID!
* */
create_unique_id (unique_id, msgidnr);
db_update_message (msgidnr, unique_id, totalmem+headersize,
0);
actiontaken = 1;
break;
}
}
}
}
return actiontaken;
}