/* 
   elmo - ELectronic Mail Operator

   Copyright (C) 2003 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 <string.h>
#include <sys/types.h>
#include <regex.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"

/****************************************************************************
 *    IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS
 ****************************************************************************/

enum field {
        FIELD_INVALID,
        FIELD_TO,
        FIELD_FROM,
        FIELD_SUBJECT,
        FIELD_CC,
        FIELD_TOCC,
};

/****************************************************************************
 *    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;

/****************************************************************************
 *    INTERFACE DATA
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES
 ****************************************************************************/
/****************************************************************************
 *    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 int
condition_met (struct constraint *c, mail_t *mail)
{
        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);
        }
        return 0;
}



static char *
find_rule (struct rule *rule, mail_t *mail)
{
        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)
                return rule->action;

        return find_rule (rule->next, mail);
}


/****************************************************************************
 *    INTERFACE FUNCTIONS
 ****************************************************************************/


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
                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)
{
        char *box = find_rule (rules, mail);

        if (box)
                return file_with_dir (mybox_dir, box);

        return mybox_inbox ();
}


/****************************************************************************
 *    INTERFACE CLASS BODIES
 ****************************************************************************/
/****************************************************************************
 *
 *    END MODULE procmail.c
 *
 ****************************************************************************/


syntax highlighted by Code2HTML, v. 0.9.1