/*
elmo - ELectronic Mail Operator
Copyright (C) 2003, 2004 rzyjontko
This program 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; version 2.
This program 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 this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
----------------------------------------------------------------------
*/
/****************************************************************************
* IMPLEMENTATION HEADERS
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <regex.h>
#include <errno.h>
#include <fcntl.h>
#include "procmail.h"
#include "debug.h"
#include "xmalloc.h"
#include "rarray.h"
#include "error.h"
#include "mail.h"
#include "mybox.h"
#include "file.h"
#include "misc.h"
#include "wrapbox.h"
#include "mlex.h"
#include "gettext.h"
#include "ask.h"
/****************************************************************************
* IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS
****************************************************************************/
#ifdef __GNUC__
# define FORMAT_2 __attribute__ ((format (printf, 2, 3)))
#else
# define FORMAT_2
#endif
enum field {
FIELD_INVALID,
FIELD_TO,
FIELD_FROM,
FIELD_SUBJECT,
FIELD_CC,
FIELD_TOCC,
FIELD_ANY,
};
/****************************************************************************
* IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES
****************************************************************************/
/****************************************************************************
* IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES
****************************************************************************/
struct constraint {
enum field field;
regex_t re;
};
struct rule {
struct rule *next;
char *name;
rarray_t *constraints;
char *action;
int stop;
};
/****************************************************************************
* IMPLEMENTATION REQUIRED EXTERNAL REFERENCES (AVOID)
****************************************************************************/
/****************************************************************************
* IMPLEMENTATION PRIVATE DATA
****************************************************************************/
/* These two variables define a list of rules. Last is used to append
to the end of the list instead of prepending. */
struct rule *rules = NULL;
struct rule *last = NULL;
/* This variable holds a new rule, while it is parsed in config file. */
struct rule *prepared = NULL;
struct constraint *constraint = NULL;
static char *logfile = NULL;
/****************************************************************************
* INTERFACE DATA
****************************************************************************/
/****************************************************************************
* IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES
****************************************************************************/
static void log (FILE *fp, const char *fmt, ...) FORMAT_2;
/****************************************************************************
* IMPLEMENTATION PRIVATE FUNCTIONS
****************************************************************************/
static void
destroy_constraint (struct constraint *c)
{
regfree (& c->re);
xfree (c);
}
static void
destroy_constraints (rarray_t *c)
{
int i;
for (i = 0; i < c->count; i++){
destroy_constraint ((struct constraint *) c->array[i]);
}
rarray_destroy (c);
}
static void
destroy_rule (struct rule *rule)
{
if (rule == NULL)
return;
destroy_rule (rule->next);
if (rule->name)
xfree (rule->name);
if (rule->constraints)
destroy_constraints (rule->constraints);
if (rule->action)
xfree (rule->action);
xfree (rule);
}
static void
store (void)
{
if (rules == NULL){
rules = prepared;
last = prepared;
}
else {
last->next = prepared;
last = prepared;
}
prepared = NULL;
}
static int
str_match (struct constraint *c, char *str)
{
int ret;
if (str == NULL)
return 0;
ret = regexec (& c->re, str, 0, NULL, 0);
if (ret && ret != REG_NOMATCH){
error_regex (ret, & c->re, NULL);
}
return ! ret;
}
static int
addr_match (struct constraint *c, address_t *addr)
{
if (addr == NULL || addr->full == NULL)
return 0;
return str_match (c, addr->full);
}
static int
raddr_match (struct constraint *c, raddress_t *ptr)
{
int i;
if (ptr == NULL)
return 0;
for (i = 0; i < ptr->count; i++){
if (addr_match (c, ptr->array[i]))
return 1;
}
return 0;
}
static void
log (FILE *fp, const char *fmt, ...)
{
va_list ap;
if (fp == NULL)
return;
va_start (ap, fmt);
vfprintf (fp, fmt, ap);
va_end (ap);
}
static int
condition_met (struct constraint *c, mail_t *mail)
{
unsigned int i;
switch (c->field){
case FIELD_INVALID:
return 0;
case FIELD_TO:
return raddr_match (c, mail->to);
case FIELD_FROM:
return addr_match (c, mail->from);
case FIELD_SUBJECT:
return str_match (c, mail->subject);
case FIELD_CC:
return raddr_match (c, mail->cc);
case FIELD_TOCC:
return raddr_match (c, mail->to)
|| raddr_match (c, mail->cc);
case FIELD_ANY:
if (mail->headers) {
for (i = 0; i< mail->headers->count; ++i) {
char *txt = mail->headers->array[i];
if (str_match (c, txt))
return 1;
}
}
return 0;
}
return 0;
}
static char *
find_rule (struct rule *rule, mail_t *mail, FILE *fp)
{
int i;
struct constraint *c;
if (rule == NULL)
return NULL;
for (i = 0; i < rule->constraints->count; i++){
c = (struct constraint *) rule->constraints->array[i];
if (! condition_met (c, mail))
break;
}
if (i == rule->constraints->count){
log (fp, _("All constraints of the rule %s have been met. "
"Delivering message to %s.\n"),
rule->name, rule->action);
return rule->action;
}
return find_rule (rule->next, mail, fp);
}
static int
write_message (char *msg, FILE *fp)
{
char *seek = msg - 1;
while (*++seek){
if (*seek == '.'){
if (seek >= msg + 2
&& seek[-1] == '\n' && seek[-2] == '\r'){
if (seek[1] == '\r' && seek[2] == '\n')
break;
else
continue;
}
}
else if (*seek == '\r'){
continue;
}
if (fputc (*seek, fp) == EOF){
error_ (errno, "%s", _("Error while writing message. "
"Message dropped."));
fclose (fp);
return 1;
}
}
return 0;
}
/****************************************************************************
* INTERFACE FUNCTIONS
****************************************************************************/
void
procmail_init (void)
{
logfile = ask_for_default ("procmail_log", NULL);
}
void
procmail_free_resources (void)
{
destroy_rule (rules);
destroy_rule (prepared);
if (constraint)
destroy_constraint (constraint);
rules = NULL;
last = NULL;
prepared = NULL;
constraint = NULL;
}
void
procmail_setup_name (char *name)
{
if (prepared != NULL)
destroy_rule (prepared);
prepared = xmalloc (sizeof (struct rule));
prepared->next = NULL;
prepared->name = xstrdup (name);
prepared->constraints = rarray_create_size (3);
prepared->action = NULL;
prepared->stop = 0;
}
void
procmail_setup_header (char *header)
{
if (prepared == NULL)
return;
if (constraint != NULL)
destroy_constraint (constraint);
constraint = xmalloc (sizeof (struct constraint));
if (strcmp (header, "SUBJECT") == 0)
constraint->field = FIELD_SUBJECT;
else if (strcmp (header, "TO") == 0)
constraint->field = FIELD_TO;
else if (strcmp (header, "FROM") == 0)
constraint->field = FIELD_FROM;
else if (strcmp (header, "CC") == 0)
constraint->field = FIELD_CC;
else if (strcmp (header, "TOCC") == 0)
constraint->field = FIELD_TOCC;
else if (strcmp (header, "ANY") == 0)
constraint->field = FIELD_ANY;
else
constraint->field = FIELD_INVALID;
}
void
procmail_setup_re (char *re)
{
int ret;
if (prepared == NULL || constraint == NULL)
return;
ret = regcomp (& constraint->re, re, REG_NOSUB | REG_NEWLINE);
if (ret){
error_regex (ret, & constraint->re, re);
destroy_constraint (constraint);
destroy_rule (prepared);
constraint = NULL;
prepared = NULL;
return;
}
rarray_add (prepared->constraints, constraint);
constraint = NULL;
}
void
procmail_setup_str (char *str)
{
char *re = misc_re_from_str (str);
procmail_setup_re (re);
xfree (re);
}
void
procmail_setup_action (char *action, int stop)
{
if (prepared == NULL)
return;
prepared->action = xstrdup (action);
prepared->stop = stop;
store ();
}
char *
procmail_box (mail_t *mail)
{
FILE *fp = NULL;
char *box;
if (logfile != NULL){
fp = fopen (logfile, "a");
if (fp == NULL)
error_ (errno, _("Couldn't open logfile %s."),
logfile);
}
box = find_rule (rules, mail, fp);
if (box == NULL){
log (fp, _("The message didn't match any rule. Delivering "
"to inbox.\n"));
}
if (fp)
fclose (fp);
if (box)
return file_with_dir (mybox_dir, box);
return mybox_inbox ();
}
void
procmail_deliver (char *message)
{
char *box;
char *fname;
FILE *fp;
while (1){
fname = wrapbox_fetch_where (1);
fp = file_open (fname, "w+", O_RDWR | O_CREAT | O_EXCL, 0600);
if (fp == NULL && errno == EEXIST)
xfree (fname);
else
break;
}
if (fp == NULL){
error_ (errno, _("Couldn't open file %s. Message dropped."),
fname);
xfree (fname);
return;
}
if (write_message (message, fp)){
xfree (fname);
return;
}
rewind (fp);
yyin = fp;
mlex_scan_file (0, 1);
fclose (fp);
box = procmail_box (newmail);
if (wrapbox_deliver_to (fname, box)){
char *inbox = mybox_inbox ();
error_ (0, _("Couldn't deliver message to %s. Moving to "
"%s instead."), box, inbox);
if (wrapbox_deliver_to (fname, inbox)){
error_ (0, _("Delivery to %s failed too. Please "
"move the file %s manually to one of "
"your boxes."),
inbox, fname);
}
xfree (inbox);
}
mail_destroy (newmail, BOX_INVALID);
xfree (box);
xfree (fname);
}
/****************************************************************************
* INTERFACE CLASS BODIES
****************************************************************************/
/****************************************************************************
*
* END MODULE procmail.c
*
****************************************************************************/
syntax highlighted by Code2HTML, v. 0.9.1