Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#include "common.h"
#include "send.h"

extern int debug;

/* 
 *      Routines for dealing with the rewrite rules.
 */

/* globals */
typedef struct rule rule;

#define NSUBEXP 10
struct rule {
        String *matchre;        /* address match */
        String *repl1;          /* first replacement String */
        String *repl2;          /* second replacement String */
        d_status type;          /* type of rule */
        Reprog *program;
        Resub subexp[NSUBEXP];
        rule *next;
};
static rule *rulep;
static rule *rlastp;

/* predeclared */
static String *substitute(String *, Resub *, message *);
static rule *findrule(String *, int);


/*
 *  Get the next token from `line'.  The symbol `\l' is replaced by
 *  the name of the local system.
 */
extern String *
rule_parse(String *line, char *system, int *backl)
{
        String *token;
        String *expanded;
        char *cp;

        token = s_parse(line, 0);
        if(token == 0)
                return(token);
        if(strchr(s_to_c(token), '\\')==0)
                return(token);
        expanded = s_new();
        for(cp = s_to_c(token); *cp; cp++) {
                if(*cp == '\\') switch(*++cp) {
                case 'l':
                        s_append(expanded, system);
                        *backl = 1;
                        break;
                case '\\':
                        s_putc(expanded, '\\');
                        break;
                default:
                        s_putc(expanded, '\\');
                        s_putc(expanded, *cp);
                        break;
                } else
                        s_putc(expanded, *cp);
        }
        s_free(token);
        s_terminate(expanded);
        return(expanded);
}

static int
getrule(String *line, String *type, char *system)
{
        rule    *rp;
        String  *re;
        int     backl;

        backl = 0;

        /* get a rule */
        re = rule_parse(s_restart(line), system, &backl);
        if(re == 0)
                return 0;
        rp = (rule *)malloc(sizeof(rule));
        if(rp == 0) {
                perror("getrules:");
                exit(1);
        }
        rp->next = 0;
        s_tolower(re);
        rp->matchre = s_new();
        s_append(rp->matchre, s_to_c(re));
        s_restart(rp->matchre);
        s_free(re);
        s_parse(line, s_restart(type));
        rp->repl1 = rule_parse(line, system, &backl);
        rp->repl2 = rule_parse(line, system, &backl);
        rp->program = 0;
        if(strcmp(s_to_c(type), "|") == 0)
                rp->type = d_pipe;
        else if(strcmp(s_to_c(type), ">>") == 0)
                rp->type = d_cat;
        else if(strcmp(s_to_c(type), "alias") == 0)
                rp->type = d_alias;
        else if(strcmp(s_to_c(type), "translate") == 0)
                rp->type = d_translate;
        else if(strcmp(s_to_c(type), "auth") == 0)
                rp->type = d_auth;
        else {
                s_free(rp->matchre);
                s_free(rp->repl1);
                s_free(rp->repl2);
                free((char *)rp);
                fprint(2,"illegal rewrite rule: %s\n", s_to_c(line));
                return 0;
        }
        if(rulep == 0)
                rulep = rlastp = rp;
        else
                rlastp = rlastp->next = rp;
        return backl;
}

/*
 *  rules are of the form:
 *      <reg exp> <String> <repl exp> [<repl exp>]
 */
extern int
getrules(void)
{
        Biobuf  *rfp;
        String  *line;
        String  *type;
        String  *file;

        file = abspath("rewrite", UPASLIB, (String *)0);
        rfp = sysopen(s_to_c(file), "r", 0);
        if(rfp == 0) {
                rulep = 0;
                return -1;
        }
        rlastp = 0;
        line = s_new();
        type = s_new();
        while(s_getline(rfp, s_restart(line)))
                if(getrule(line, type, thissys) && altthissys)
                        getrule(s_restart(line), type, altthissys);
        s_free(type);
        s_free(line);
        s_free(file);
        sysclose(rfp);
        return 0;
}

/* look up a matching rule */
static rule *
findrule(String *addrp, int authorized)
{
        rule *rp;
        static rule defaultrule;

        if(rulep == 0)
                return &defaultrule;
        for (rp = rulep; rp != 0; rp = rp->next) {
                if(rp->type==d_auth && authorized)
                        continue;
                if(rp->program == 0)
                        rp->program = regcomp(rp->matchre->base);
                if(rp->program == 0)
                        continue;
                memset(rp->subexp, 0, sizeof(rp->subexp));
                if(debug)
                        fprint(2, "matching %s against %s\n", s_to_c(addrp),
                                rp->matchre->base);
                if(regexec(rp->program, s_to_c(addrp), rp->subexp, NSUBEXP))
                if(s_to_c(addrp) == rp->subexp[0].sp)
                if((s_to_c(addrp) + strlen(s_to_c(addrp))) == rp->subexp[0].ep)
                        return rp;
        }
        return 0;
}

/*  Transforms the address into a command.
 *  Returns:    -1 ifaddress not matched by reules
 *               0 ifaddress matched and ok to forward
 *               1 ifaddress matched and not ok to forward
 */
extern int
rewrite(dest *dp, message *mp)
{
        rule *rp;               /* rewriting rule */
        String *lower;          /* lower case version of destination */

        /*
         *  Rewrite the address.  Matching is case insensitive.
         */
        lower = s_clone(dp->addr);
        s_tolower(s_restart(lower));
        rp = findrule(lower, dp->authorized);
        if(rp == 0){
                s_free(lower);
                return -1;
        }
        strcpy(s_to_c(lower), s_to_c(dp->addr));
        dp->repl1 = substitute(rp->repl1, rp->subexp, mp);
        dp->repl2 = substitute(rp->repl2, rp->subexp, mp);
        dp->status = rp->type;
        if(debug){
                fprint(2, "\t->");
                if(dp->repl1)
                        fprint(2, "%s", s_to_c(dp->repl1));
                if(dp->repl2)
                        fprint(2, "%s", s_to_c(dp->repl2));
                fprint(2, "\n");
        }
        s_free(lower);
        return 0;
}

static String *
substitute(String *source, Resub *subexp, message *mp)
{
        int i;
        char *s;
        char *sp;
        String *stp;
        
        if(source == 0)
                return 0;
        sp = s_to_c(source);

        /* someplace to put it */
        stp = s_new();

        /* do the substitution */
        while (*sp != '\0') {
                if(*sp == '\\') {
                        switch (*++sp) {
                        case '0': case '1': case '2': case '3': case '4':
                        case '5': case '6': case '7': case '8': case '9':
                                i = *sp-'0';
                                if(subexp[i].sp != 0)
                                        for (s = subexp[i].sp;
                                             s < subexp[i].ep;
                                             s++)
                                                s_putc(stp, *s);
                                break;
                        case '\\':
                                s_putc(stp, '\\');
                                break;
                        case '\0':
                                sp--;
                                break;
                        case 's':
                                for(s = s_to_c(mp->replyaddr); *s; s++)
                                        s_putc(stp, *s);
                                break;
                        case 'p':
                                if(mp->bulk)
                                        s = "bulk";
                                else
                                        s = "normal";
                                for(;*s; s++)
                                        s_putc(stp, *s);
                                break;
                        default:
                                s_putc(stp, *sp);
                                break;
                        }
                } else if(*sp == '&') {                         
                        if(subexp[0].sp != 0)
                                for (s = subexp[0].sp;
                                     s < subexp[0].ep; s++)
                                        s_putc(stp, *s);
                } else
                        s_putc(stp, *sp);
                sp++;
        }
        s_terminate(stp);

        return s_restart(stp);
}

extern void
regerror(char* s)
{
        fprint(2, "rewrite: %s\n", s);
        /* make sure the message is seen locally */
        syslog(0, "mail", "regexp error in rewrite: %s", s);
}

extern void
dumprules(void)
{
        rule *rp;

        for (rp = rulep; rp != 0; rp = rp->next) {
                fprint(2, "'%s'", rp->matchre->base);
                switch (rp->type) {
                case d_pipe:
                        fprint(2, " |");
                        break;
                case d_cat:
                        fprint(2, " >>");
                        break;
                case d_alias:
                        fprint(2, " alias");
                        break;
                case d_translate:
                        fprint(2, " translate");
                        break;
                default:
                        fprint(2, " UNKNOWN");
                        break;
                }
                fprint(2, " '%s'", rp->repl1 ? rp->repl1->base:"...");
                fprint(2, " '%s'\n", rp->repl2 ? rp->repl2->base:"...");
        }
}