#module ADD_TRANSFORM "V5.6"

/*
**++
**  FACILITY:
**
**      ADD_TRANSFORM
**
**  ABSTRACT:
**
**      This module converts ann internet-style mail addresses and DECnet
**      format mail addresses into a form usable by the local VMS MAIL
**      utility. This normally takes the form of recognising local addresses,
**      and using the local username as the address, recognising addresses which
**      map to users on the local DECnet, and using a DECnet-format address,
**      and sending all other address types to a mail gateway.
**
**      This code has been set up to read an address configuration file at
**      startup, and apply the configuration rules to the internet addresses to
**      produce a correct address for the VMS MAIL system.
**
**      You may either:
**          - Use the default configuration (All addresses are directed to the
**            local PMDF mail protocol handler - in%"address").
**
**          - Edit the default configuration to match your requirements.
**
**          - create a local configuration file (news_manager:news_address.cnf)
**            and use that file to set up local address configuration rules .
**
**            The file has the following format:
**              # (in column 1) is a comment line - the line is ignored
**              internet_address rebuild_rule
**              internet_address rebuild_rule
**              internet_address rebuild_rule
**
**            The internet address field must start in column 1 and must not
**            contain space characters. The '*' character may be used to force
**            a wildcard match with the address being tested. The '?' character
**            matches any single character.
**
**            The rebuild rule must be separated from the internet address by
**            at least one space character. The '\' character is used as the
**            escape character: normally it is used to specify match
**            subsitiutions - the '\' character itself is specified by the
**            sequence '\\'.
**
**            e.g.:
**              # this is an example address cnf file
**              *@*.anu.* \002::\001
**              *@*.anu   \002::\001
**              *@*       "gateway::in%""\001@\002"""
**              *::*      \001::\002
**              *         \001
**
**            The rules are applied in order until a succesful match is made
**            with an internet address form - then the matching rebuild rule is
**            selected to transform that address into a form acceptable to VMS
**            MAIL.
**
**            Control characters in the rebuild pattern of the first successful
**            address match correspond to substitution of the original
**            substring which matched at the corresponding wildcard position.
**            eg:
**                  pattern = "*@fred.*!*"
**                  rebuild = "\002-\003|BB\001|\002'"
**
**                  input   = user@fred.example!last
**                            ----      ------- ----
**                            1         2       3
**               gives:
**                  output  = example-last|BBuser|example'
**                            ------- ----   ---- -------
**                            2       3      1    2
**
**          - Edit/rewrite this module to do anything else you want - as long
**            as you provide the same entry points, entry point parameters,
**            and the same return values as the procedures supplied here.
**
**
**  AUTHOR:
**
**      Geoff Huston
**
**  COPYRIGHT:
**
**      Copyright © 1988
**
**  MODIFICATION HISTORY:
**
**      V5.5     7-Oct-1988     GIH
**          Change parsing of news_address.cnf to allow tabs as seperators
**      V5.6    11-Nov-1988     GIH
**          Change default PMDF rule set to leave in%* and psi%* address
**          formats unaltered.
**--
**/

#include stdio
#include ctype
#include "newsdefine.h"

                        /* Local Variables */

static int sm[20],      /* markers used in wild card match and substitution. */
           em[20];      /* sm is an array of start positions, em is an array */
                        /* of end position of the wildcard match points      */


static char retadd[132];    /* the return address is built in this variable  */

static struct {         /* the structure which holds the address patterns    */
    char *internet,     /* and the corresponding rewrite rules               */
         *local;
    } def_rules[] = {   /* Default is to pass all addresses to local PMDF    */
        {"in%*",    "in%\001"},
        {"psi%*",   "psi%\001"},
        {"@*",      "@\001"},
        {"*@*",     "\"in\%\"\"\001@\002\"\"\""},
        {"*::*",    "\"in\%\"\"\002@\001\"\"\""},
        {"*",       "\001"},
        {"",""}},
      *addrs = 0;       /* Pointer to loaded rule set                        */

/*
 *++
 *  Description:
 *      Read the local address configuration file into a memory structure
 *
 *  Parameters:
 *      None
 *
 *  Side effects:
 *      Sufficient memory for the config rules is allocated from free memory
 *
 *  Exceptions:
 *      None
 *
 *  Result:
 *      Struct pointer addrs points to loaded address rule set
 *--
 */

init_transform()
{
    FILE *fpr;
    char inline[256],
         result[256],
         *c;
    int size = 0,
        malloc_size = 0;

    sysprv();                       /* turn on sysprv to addess config file */
    if (fpr = fopen(ADDRESS_FILE,"r")) {    /* Check if file is accessible  */
        char *c1,
             *c2;

        while (fgets(inline,256,fpr)) {     /* read file line-by-line       */
            if (*inline == '#') continue;   /* skip comment lines           */

            if (c = strchr(inline,'\n')) *c = '\0'; /* strip trailing lf char */

                      /* divide into two parts using white chars as separator */
            c = inline;
            while (!isspace(*c)) c++;
            while (isspace(*c)) *c++ = '\0';

            c1 = c;     /* pick out '\...' substrings and convert to numeric */
            c2 = result;
            while (*c1) {
                if (*c1 != '\\') *c2++ = *c1++;
                else {
                    c1++;
                    if (isdigit(*c1)) { /* found '\nnn' substring - convert using octal */
                        char *s = c1,
                             sav;
                        int tmp;

                        while (isdigit(*s)) s++;
                        sav = *s;
                        *s = '\0';
                        if (sscanf(c1,"%o",&tmp) == 1) *c2++ = tmp;
                        *s = sav;
                        c1 = s;
                        }
                    else *c2++ = *c1++;  /* else simply use next char literally */
                    }
                }
            *c2 = '\0';
            c = result;

                /* now add to rule set */
            if (!malloc_size) addrs = malloc((malloc_size = 5)  * (sizeof *addrs));
            else if (size >= malloc_size) addrs = realloc(addrs,(malloc_size += 5) * (sizeof *addrs));
            strcpy((addrs[size].internet = malloc(strlen(inline) + 1)),inline);
            strcpy((addrs[size].local = malloc(strlen(c) + 1)),c);
            ++size;
            }

            /* if no rules read, then load default rule */
        if (!size) {
            if (!malloc_size) addrs = malloc((malloc_size = 2)  * (sizeof *addrs));
            else if (size >= malloc_size) addrs = realloc(addrs,(malloc_size += 2) * (sizeof *addrs));
            strcpy(inline,"*");
            c = "\001";
            strcpy((addrs[size].internet = malloc(strlen(inline) + 1)),inline);
            strcpy((addrs[size].local = malloc(strlen(c) + 1)),c);
            ++size;
            }

            /* add emtpy (terminating) rule */
        *inline = '\0';
        c = inline;
        if (!malloc_size) addrs = malloc((malloc_size = 5)  * (sizeof *addrs));
        else if (size >= malloc_size) addrs = realloc(addrs,(malloc_size += 1) * (sizeof *addrs));
        strcpy((addrs[size].internet = malloc(strlen(inline) + 1)),inline);
        strcpy((addrs[size].local = malloc(strlen(c) + 1)),c);

        fclose(fpr);
        }

        /* no .cnf file - use default rule set */
    else addrs = def_rules;
    nosysprv();
}

/*
 *  transform
 *
 *  Build the equivalence string using the rebuilding rule corresponding
 *  to the successfully matched pattern. sm and em now contain the
 *  wildcard match points in the original input string
 */

static char *transform(locname,i)
    char *locname;
    int i;
{
    int j = 0,
        k,
        r = 0;
    char lc;

    while (lc = addrs[i].local[j]) {
        if (lc < 20) {
            for (k = sm[lc - 1]; k <= em[lc - 1]; ++k)
                retadd[r++] = locname[k];
            }
        else retadd[r++] = addrs[i].local[j];
        j++;
        }
    retadd[r] = '\0';
    return(retadd);
}

/*
 *  match
 *
 *  Recursive match procedure which performs wildcard matching.
 */

static match(l,p,i,li)
    char *l,
         *p;
    int i,
        li;
{
    if (!*l) {
        if (!*p) return(1);
        else if (*p == '*') {
            sm[i] = 0;
            em[i] = -1;
            return(match(l,p+1,i+1,li));
            }
        else return(0);
        }
    if (*p == '*') {
        sm[i] = li;
        em[i] = li - 1;
        while (!match(l,p+1,i+1,li)) {
            l++;
            li++;
            em[i] = li - 1;
            if (!*l) {
                if (!*(p+1)) return(1);
                else return(0);
                }
            }
        return(1);
        }
    if (*p == '?') return(match(l+1,p+1,i,li+1));
    return((*l == *p) && match(l+1,p+1,i,li+1));
}

/*
 *  add_transform
 *
 *  Entry point. Returns the VMS Mail equivalent address of the
 *  supplied internet address.
 */

char *add_transform(s)
    char *s;
{
    char locname[132],
         *p;
    int i = 0;

    if (p = strchr(s,'<')) {
        strcpy(locname,++p);
        if (p = strchr(locname,'>')) *p = '\0';
        if (p = strchr(locname,'\n')) *p = '\0';
        }
    else {
                                /* strip leading blanks */
        while (*s == ' ') s++;
        strcpy(locname,s);

                                /* strip trailing fields (this should be a
                                   personal name if present) */
        if (p = strchr(locname,' ')) *p = '\0';
        if (p = strchr(locname,'(')) *p = '\0';
        }

            /* convert to lower case */
    p = locname;
    while (*p) { *p = tolower(*p); p++; }

        /* no transform rules loaded - return address */
    if (!addrs) return(s);

        /* find a match, and return the transform */
    while (*(addrs[i].internet)) {
        if (match(locname,addrs[i].internet,0,0))
            return(transform(locname,i));
        i++;
        }

    return(s);
}
