Sample Router Configuration Scripts

ZMailer: The Manual; v1.99.20
Prev |  | Next

# Appendix A. Sample Router Configuration Scripts

Text to be inserted here.

The following are examples of the router configuration scripts SMTP+UUCP.cf, crossbar.cf, process.cf, and rrouter.cf.

# A.1. `SMTP+UUCP.cf`

ZMailer 2 configuration file for a generic SMTP host (with UUCP links)

    ZCONFIG=@ZMAILERCFGFILE@

    . $ZCONFIG

    PATH=.:$MAILSHARE/cf:$MAILBIN/bin ; export PATH
    PS1=z$PS1

Configure error logging (squirrel)

    squirrel -breakin
    squirrel badheader

Domains with these toplevels will not be canonicalized via DNS lookup. This list is from ISOC table of 16-April-95.

The quoted string {\tt "ad...zw"} should be on one line in the actual SMTP+UUCP.cf file.

    toplevels="ad ae af ag ai al am an ao aq ar as at au aw az ba bb bd
               be bf bg bh bi bj bm bn bo br bs bt bv bw by bz ca cc cf
    	   cg ch ci ck cl cm cn co com cr cu cv cx cy cz de dj dk dm
    	   do dz ec edu ee eg eh es et fi fj fk fm fo fr ga gb gd ge
    	   gf gh gi gl gm gn gov gp gq gr gt gu gw gy hk hm hn hr ht
    	   hu id ie il in int io iq ir is it jm jo jp ke kg kh ki km
    	   kn kp kr kw ky kz la lb lc li lk lr ls lt lu lv ly ma mc
    	   md mg mh mil ml mm mn mo mp mq mr ms mt mu mv mw mx my mz
    	   na nc ne net nf ng ni nl no np nr nt nu nz om org pa pe pf
    	   pg ph pk pl pm pn pr pt pw py qa re ro ru rw sa sb sc sd se
    	   sg sh si sj sk sl sm sn so sr st sv sy sz tc td tf tg th tj
    	   tk tm tn to tp tr tt tv tw tz ua ug uk um us uy uz va vc ve
    	   vg vi vn vu wf ws ye yu za zm zr zw"

The transport preference order

    protocols='routes smtp uucp'

Will the MAILVAR/lists/listname show out sender identity as either: owner-listname, or: listname-owner?

    if true ; then # Change to "false" to get "pre-owner" mode
            preowner=""
            postowner="-owner"
    else
            preowner="owner-"
            postowner=""
    fi

Does our ``local'' channel accept domain (@) at the user part? ZMailer's mailbox does accept. If you use something else, and it doesn't accept, comment this away.

    localdoesdomain=1

We may want .forward and mailing list files to be private, i.e., we ignore the current privileges when checking the privileges of such files. Don't add `include' to this list, since anyone can :include: any file.

    private='.forward maillist'

Set up dependency checking.

    . consist.cf
    require siteinfo router crossbar process server

The following are standard setup files and must be loaded in this order

    . standard.cf
    . trusted.cf

Load the databases so they and the variables defined (e.g. network-specific node names for this host) can be used in the site specific configuration.

    for method in $protocols
    do
            test -f $MAILSHARE/cf/i-${method}.cf && . i-${method}.cf
    done

    mailconf () {
            local hname

            # My official hostname
            if [ -f /bin/hostname ]; then
                    rawhostname=$(/bin/hostname)
            elif [ -f /etc/sys_id ]; then
                    read rawhostname < /etc/sys_id
            else
                    rawhostname=$(/bin/uname -n)
            fi

            hname=$(canon $rawhostname)

Try to discover the organizational domain name.

            orgdomain=$hname
            sift $hname in
            $rawhostname\.(.+)
                    orgdomain=\1
                    ;;
            tfis
            hostname=$hname

            # This is what it will say on out mail
            mydomain=$hostname
    }

    orgdomains=x
    : ${MAILCONF:=/etc/mail.conf}
    if [ ! -r $MAILCONF ]; then
            echo "$0: missing $MAILCONF: using the following values:"
            mailconf
            echo orgdomain=$orgdomain
            echo hostname=$hostname
            echo mydomain=$mydomain
            provide siteinfo
    else
            . $MAILCONF && provide siteinfo
    fi
    [ "$orgdomains" = x ] && orgdomains=$orgdomain

Set hostname to enable message-id generation and checking.

    hostname $hostname

    . aliases.cf
    . canon.cf
    . rrouter.cf
    . crossbar.cf

    for method in $protocols
    do
            . p-${method}.cf
    done

    . process.cf
    . server.cf

    consist || exit 1

# A.2. `Crossbar.cf`

    provide crossbar

The crossbar function makes the policy decisions of how the instance of a message between a particular sender and recipient should be treated. The 'from' and 'to' parameters are quads, i.e., in the form

(channel host user attributes)

The function may modify any of these elements of both the from and to addresses, and must select a message header address rewriting function to be applied to this message instance. If the return value is nil or empty, the instance is completely ignored, to the point that if there are no other recipients specified a complaint will be generated saying there are {\bf no} recipients specified.

    crossbar (from, to) {
            local rewrite destination tmp

Count them... (in {\tt process.cf}) (we could use this as an ultimate duplicate remover too...)

            db add recipients "$(user $to)" "$(user $from)"

Intercept (drop, redirect, bounce, save) the message

            tmp=$(intercept "$(user $from)") &&
                    case "$(car $tmp)" in

Dropping error types for from addresses is necessary to avoid mail loops.

                    drop|error)
                            return ;;
                    file)   LOGMSG="$LOGMSG $(car $(cdr $tmp))" ;;
                    esac

Only intercept mail that is not from the local postmaster, so that error messages can find their way back.

            [ "$(channel $from)" = local -a "$(user $from)" = postmaster ] ||
            tmp=$(intercept "$(user $to)") &&
                    case "$(car $tmp)" in
                    drop)   return ;;
                    error)  setf $(channel $to) error
                            setf $(host $to) $(car $(cdr $tmp))
                            ;;
                    file)   LOGMSG="$LOGMSG $(car $(cdr $tmp))" ;;
                    esac

If we do any alias expansion from the crossbar, we should do this:

    db flush expansions

Determine which rewrite function (for message header addresses) to use.

            case $(channel $to) in
            smtp|smtpx)
                    #case "$(channel $from)" in
                    #smtp|smtpx)    # Address should be forwarded the way the arrive
                    #       rewrite=null ;;
                    #*)     rewrite=internet ;;
                    #esac
                    rewrite=internet
                    ;;
            error)  rewrite=null ;;
            local)  case "$(channel $from)" in
                    local)  #rewrite=intramachine
                            rewrite=internet ;;
                    *)      # addresses should be saved the way they arrive
                            rewrite=null ;;
                    esac
                    ;;
            usenet) rewrite=internet ;;
            ean)    rewrite=ean_useratdomain ;;
            *)      # This is usually UUCP or BITNET
                    # We want to determine the final destination host/domain
                    destination="$(uucproute "$(user $to)")"
                    if [ "$(host $to)" ]; then
                            destination="$(host $to)"!"$destination"
                    fi
                    sift "$destination" in
                    .*!([^!]+)![^!]+
                            destination="\1" ;;     # destination domain
                    .*\.(bitnet|netnorth|earn|cdn)
                            rewrite=smtp_useratdomain
                            break ;;                        # reply to user@domain
                    .*      rewrite=internet ; break ;;     # default sensible thing
                    tfis
                    ;;
            esac

The alias expansion might want to modify the envelope sender of the message instance. Here we cooperate in the scheme which is to set the 'sender' attribute of the destination address.

            tmp="$(get $(attributes $to) sender)" && [ x"$tmp" != x ] &&
                    from=(local "$tmp" "$tmp" $(attributes $from))

            case "$(channel $from)" in
            defrt1*)
                    setf "$(user $from)" "$(bitnetroute "$(user $from)")"
                    if [ $rewrite = internet ]; then
                            rewrite=bitnet2internet
                    fi
                    ;;
            esac

Rewrite the envelope addresses appropriately.

            case "$(channel $to)" in
    #       uucp|local)
            uucp)

Local destination on a system that delivers in UCB Mail compatible mail spool files means that the From_ line must be in all-! form, which is the same as the UUCP transport requirement.

                    setf "$(user $from)" "$(uucproute "$(user $from)")"
                    setf "$(user $to)" "$(uucproute "$(user $to)")"
                    sift "$(user $to)" in
                    (.)!(.*)        if [ \1 = $(host $to) ]; then
                                            setf "$(user $to)" \2
                                    fi
                                    ;;
                    tfis
                    sift "$(user $to)" in
                    (.)\.uucp!(.*)  setf "$(user $to)" \1!\2 ;;
                    tfis
                    ;;
    #       smtp)
            smtp|smtpx|local|bsmtp3*)
                    tmp="$(smtproute "$(user $from)")"
                    sift "$tmp" in
                    (@$hostname[:,].*)|([^@:,]+@$hostname)
                            break ;;
                    .*
                            # tmp="@$hostname:$tmp"  # <-- that creates RFC-822
                                                     #     source-routing, AVOID!
                            tmp="$tmp"
                            ;;
                    @(.+):(.+:.+)
                            tmp="@\1,\2" ; continue ;;
                    tfis
                    setf "$(user $from)" "$tmp"
                    sift "$(user $to)" in
                    (^/).*  setf "$(user $to)" "$(smtproute "$(user $to)")" ;;
                    tfis

                    ;;
            ean)
                    setf $(user $from) "$(ean_useratdomain "$(user $from)")"
                    setf "$(user $to)" "$(ean_useratdomain "$(user $to)")"
                    ;;
            usenet)
                    setf $(user $from) "$(uucproute "$(user $from)")"
                    sift $(user $from) in
                    $hostname!.*    ;;
                    .*      setf $(user $from) $hostname!$(user $from) ;;
                    tfis
                    # newsgroup name only
                    setf "$(user $to)" $(localpart "$(user $to)")
                    ;;
    #       bsmtp3|bsmtp3nd)
    #               setf $(user $from) "$(bitnetroute "$(user $from)")"
    #               tmp="$(bitnetroute "$(user $to)")"
    #               sift "$tmp" in
    #               .*@([^.]).uucp
    #                       tmp="$(bitnetShortroute "$(user $to)")" ;;
    #               tfis
    #               setf "$(user $to)" "$tmp"
    #               rewrite=bitnetShortroute
    #               ;;

            defrt1)
                    setf $(user $from) "$(bitnetroute "$(user $from)")"
                    setf $(user $to) "$(bitnetroute "$(user $to)")"
                    rewrite=bitnetroute
                    sift "$(user $to)" in
                    (.*)[!%](.+)@(.*)
                            to=(error bitnetgw "\3" $(attributes $to))
                            rewrite=null
                            ;;
                    tfis
                    ;;
            esac

            #log recipient: "$(channel $to)" "$(host $to)" "$(user $to)"
            return ($rewrite $from $to)
    }       # end of crossbar

If you want to intercept specific mail messages, this function and the associated code in the crossbar and process functions will let you do it. There are three possible actions:

       drop      - completely ignore this address
       error     - return the specified error message
       file      - append the message file to the specified file

Both the file and error actions require an argument, which necessitates the use of multiple-value return (i.e., return a list) in all cases.

If you don't want to intercept anything, this function should return failure. The stub defined here is the usual case, you can override it in the host- specific cf file.

    intercept (address) {
    #       case "$(smtp_useratdomain "$address")" in
    #       *@pdq*)         return (file /var/scr/pdq) ;;
    #       rayan@csri.*)   return (drop) ;;
    #       bitftp*@*)      return (error bounce) ;;
    #       esac

            return 1
    }

On mail from one local user to another, we don't want to see all the long domain name extensions. This can cause problems with silly UAs, if it does you can just redefine {\tt intramachine} to call {\tt null} in your site or host-specific configuration files.

    intramachine (address) {                # strip hostname if it came from here
            sift "$address" in
            (.*)@($hostname|$mydomain)
                    address="$(condquote "\1")" ;;
            tfis
            return "$address"
    }       # end of intramachine

    null (address) {
            return "$address"               # surprise!
    }

This is usually the default message-header address rewriting function. It is responsible for hostname hiding and qualification.

    internet (address) {
            address="$(canonicalize "$address")"    # Canonicalize does local
                                                    # hostname hiding...
            sift "$address" in
            (.*)<@(.+)>(.*)
                            #if [ $(deliver \2) ]; then     # hostname hiding
                            #       address="\1@${mydomain}\3"
                            #       break
                            #fi
                            address="\1@\2\3" # No hostname hiding...
                            ;;
            (.*)<(.+)>(.*)  address="\1\2\3" ;;             # defocus
            [^@]+
                    # This is a local part address w/o any domains!
                    address="$(condquote "$address")"
                    address="$address@$mydomain"    # add our hostname
                    ;;
            tfis
            return "$address"
    }       # end of internet

# A.3. `Process.cf`

This is the protocol switch function. It keys off the form of the filename to determine how to process a particular class of messages. It is expected that an internal function will be called to orchestrate the processing of the message and enforce proper semantics.

The file argument is the name of a file in the {\tt \$POSTOFFICE/router} directory.

    process (file) {

            db flush pwuid
            db flush pwnam
            db flush fullname
            db flush hostexpansions
            db flush recipients

Since we cannot detect that the password database has been updated under our feet, we flush the cached information every once in a while (in this case, before every message).

            LOGMSG=''

The LOGMSG variable is used by the intercept facility (in {\tt crossbar.cf}) to make sure only a single copy of a message is saved when required. Each sender - recipient address pair can cause an intercept which can specify a file to save the message to. This variable is appended to elsewhere, and processed at the end of this function.

            case "$file" in
    #       [0-9]*.x400)    x400 "$file" ;;
    #       [0-9]*.uucp)    uucpfilter "$file" > /tmp/X.$$
    #                       cat /tmp/X.$$ > "$file"
    #                       rfc822 "$file" ;;
            [0-9]*)         rfc822 "$file" ;;
            core*)          /bin/mv "$file" ../$file.router.$$
                            return
                            ;;
            *)              /bin/mv "$file" ../postman/rtr."$file".$$
                            return
                            ;;
            esac

            [ $? ] && return 0      # Leave when they returned failure..

The file names in the {\tt \$POSTOFFICE/router} directory are determined by the parameter to the mail\_open() C library routine. This case statement knows about the various message file types needed on your system, and arranges appropriate processing of each. The internal function {\tt rfc822} expects a file name as argument, and determines the semantics of the message and of the configuration code. For example, the {\tt router}, {\tt crossbar}, and {\tt header\_defer} functions have semantics only because the {\tt rfc822} function knows about them. There are no other message formats supported in this distribution.

            log info: recipients $(db count recipients) $(elements $(cdr $(cdr $(cdr $(cdr $envelopeinfo))))) '
    '

For statistics gathering we print out the envelope information property list in its entirety, except for the file name, and the message id, both of which were logged earlier (in C code).

            for f in $LOGMSG
            do
                    { echo "==${file}==$(rfc822date)==" ;
                      /bin/cat ../queue/"$file" } >> $f && log saved "$file" in $f
            done
    }

This does the saving of intercepted messages into archive files.

# A.4. `Rrouter.cf`

Most of the address routing processing is done here.

    provide rrouter

    . fqdnalias.cf # Pick that set of tools into here!

    envelopeinfo=(message-id "<$USER.interactive@$hostname>" now 0)

    : ${UNRESOLVABLEACTION:='error unresolvable'}

    relation -bt selfmatch selfmatch

    rrouter (address, origaddr, A, plustail, domain) {
            local tmp tee didhostexpand priv nattr a
            # local seenuucp seenbitnet
            # seenuucp=false
            # seenbitnet=false
            didhostexpand="";
    # echo "rrouter: address=$address, origaddr=$origaddr" >> /dev/tty

            tmp=$(fqdn_neighbour "$origaddr" "$address" $A) &&
                    return $tmp

            address="$(condquote "$address")"

We have troublesome addresses coming here...

            #       "|pipe-program"
            #       "|quoted string"@domain
            #       "foo > faa"@domain
            #       "fii < fuu"@domain
            #       "foo @ faa"@domain
            #       "|foo @ faa"

and we want to do correct focusing...

            ssift "$address" in
            # Now make canonical
            '"'(.*)'"'<(.*)
                    address="\1\2"                  # defocus
                    ;;
            '"'(.*)'"'>(.*)
                    address="\1\2"                  # defocus
                    break ;;
            ([\'"'].*[\'"'])<(.*)
                    address="\1\2" ;;               # defocus
            ([\'"'].*[\'"'])>(.*)
                    address="\1\2" ;;               # defocus
            # See that it does not start with a pipe ...
    #       \|.+    # Looks like a pipe... Don't mutilate it!
    #               break ;;
    #       '"'[|].+        # Quoted pipe??   What the ...??
    #               break ;;
            tfiss

            address=$(canonicalize "$address")

            ssift "$address" in
    #       <in%>(.*)
    #               return (((error vms-in-pros "in%\1" $A))) ;;
    #       (.*)<@(.+)\.uucp>(.*)
    #               seenuucp=true
    #               address="\1$plustail<@\2>\3" ;;         # fix host.uucp!route
    #       (.*)<@(.+)\.(bitnet|earn|netnorth)>(.*)
    #               seenbitnet=true         # Strip off the (bitnet|netnorth|earn)
    #               address="\1<@\2>\4" ;;          # fix host.bitnet!route
    #        handle special cases.....
    #       \\(.)   return (((local - "$address" $A))) ;;
            @       # handle <> form???
                    tmp=(local user postmaster $A)
                    return $(routeuser $tmp "")
                    ;;

The following two are two approaches to the same problem, generally speaking we should use the SECOND one, but your mileage may vary... (Problems exist when WE are the target..)

    #       (.*)<@\[(.)\]>(.*)
    #               address="\1$plustail<@$(gethostbyaddr \2)>\3"
    #               ;;
            (.*)<@\[(.)\]>(.*)
                    # numeric internet spec
                    if [ $(selfmatch "\2") ]; then
                            address="\1$plustail<@>\3"
                            domain="@[\2]"
                            plustail=""
                    else
                            return (((smtp "[\2]" "\1$plustail@$(gethostbyaddr \2)\3" $A)))
                    fi
                    ;;

This is the end of the $1.2.3.4$ address case...

            (.*)<@(.*)\.>(.*)
                    address="\1$plustail<@\2>\3"
                    plustail=""
                    ;;

Now massage the local info.

            (.*)<@(.*)($orgdomains)>(.*)
                    address="\1$plustail<@\2$orgdomain>\4"
                    domain="@\2$orgdomain"
                    plustail=""
                    ;;
            <@(.*)>[:,](.+)@(.+)
                    if [ $(deliver "\1") ]; then # Source routed to our name?
                            return $(rrouter "\2$plustail@\3" "$origaddr" $A "" "")
                    fi
                    ;;
            <@($orgdomains)>[:,](.+)@(.+)
                    return $(rrouter "\2$plustail@\3" "$origaddr" $A "" "")
                    ;;      # strip organization
            (.+)<@(.+)>(.*)
                    if [ $(deliver "\2") ]; then    # Do we handle this?
                            address="\1$plustail<@>\3"
                            domain="@\2"
                            plustail=""
                    elif [ "\2" = "$hostname" ]; then # Is it at local host?
                            address="\1$plustail<@>\3" # (this is a backup test)
                            domain="@\2"
                            plustail=""
                    fi ;;
            <@>.(.+)        # This plustail is propably wrong...
                    return $(rrouter "\1$plustail" "$origaddr" $A "" "$domain") ;;  # try after route strip
            (.+)<@>
                    if [ -z "$domain" ]; then
                            domain="$mydomain"
                    fi
                    return $(rrouter "\1$plustail" "$origaddr" $A "" "$domain") ;;  # strip trash & retry
            tfiss

    #log "BITNET name=$bitnetname, address=$address"
            case $bitnetname in
            ?*)     tsift "$address" in
                    (.*)<@(.*)\.(bitnet|netnorth|earn)>(.*)
                            address="\1<@\2>\4" ;;
                            # Strip off the (bitnet|netnorth|earn)
                    tfist
                    ;;
            esac
    #log "BITNET name=$bitnetname, address=$address"

Resolve names to routes, get the actual channel name mostly from an external database.

            ssift "$address" in
            (.*)<@(.+)>(.*)
    #log "neighbourg test: domain: \2, addr: $address"
                    address="\1$plustail@\2\3"
                    plustail=""

If you want to have the SMARTHOST to pick the routing for all non-local stuff, enable the following test case..

                    # if [ "$SMARTHOST" ]; then
                    #       return $(rrouter "$SMARTHOST!$(uucproute "$address")$plustail" "$origaddr" $A "" "$domain")
                    # else
                    #       return ((($UNRESOLVABLEACTION "$address" $A)))
                    # fi

                    #if [ x$seenbitnet = xtrue ]; then
                    #       address="\1@\2.bitnet"
                    #fi

                    didhostexpand=$(hostexpansions "\2")

                    for method in $protocols
                    do
                            tmp=$(${method}_neighbour "\2" "$address" $A) &&
                                    return $tmp
                    done

                    #if [ x$seenuucp = xtrue ]; then
                    #       if [ "$UUCPACTION" != "" ]; then
                    #               return ((($UUCPACTION "\1@\2.uucp" $A)))
                    #       fi
                    #       tmp=$(routes_neighbour "\2.uucp" "$address" $A) &&
                    #               return $tmp
                    #fi

                    #if [ x$seenbitnet = xtrue ]; then
                    #       if [ "$BITNETACTION" != "" ]; then
                    #               return ((($BITNETACTION "\1@\2.BITNET" $A)))
                    #       fi
                    #fi

                    if [ "$SMARTHOST" ]; then
                            return $(rrouter "$SMARTHOST!$(uucproute "$address")" "$origaddr" $A "" "$domain")
                    else
                            return ((($UNRESOLVABLEACTION "$address" $A)))
                    fi
                    ;;

            \\(.+)  # A back-quote prefixed userid (most likely)
                    return $(rrouter "\1" "$origaddr" $A "$plustail" "")
                    ;;

            /.+     # file

Well, it could be a slash-notated X.400 address too..

                    return (((local "file.$origaddr" "$address" $A)))
                    ;;
            \|.+    # pipe
                    return (((local "pipe.$origaddr" "$address" $A)))
                    ;;
            :include:.+ # ":include:" -alias

We must test this here, because the file-path after this prefix may have a dot.

                    tmp=(local "$origaddr" "$address" $A)
                    return $(routeuser $tmp "")
                    ;;

Ok, from now on if we don't have a domain set, we use {\tt \$mydomain}

            .*      if [ -z "$domain" ] ; then
                            domain="@$mydomain"
                    fi
                    ;;
            (.+\.[^+]+)(\+.+)  # Dotfull name with a plus!
                    plustail="\2"
                    address="\1"

Fall forward for the dotfull processing.

                    ;;
            .+\..+  # A dotfull name
                    tmp="$(fullnamemap "$address")" && \
                            return $(rrouter "$tmp" "$origaddr" $A "$plustail" "$domain")
                    if [ $(newsgroup "$address") ]; then
                            return (((usenet - "$address" $A)))
                    fi

Okay... Not in our special fullname/newsgroup-files, lets see if it is in the traditional one?

                    if [ $(aliases "$address") ]; then

It can be found from the normal aliases, run the alias processing.

                            tmp=(local "$origaddr" "$address" $A)
                            return $(routeuser $tmp "$domain")
                    fi
                    return (((error norealname "$address" $A)))
                    return (((error nonewsgroup "$address" $A)))
                    ;;

            .*      # Now all the rest of the cases..
                    tmp=(local "$origaddr" "$address$plustail" $A)
                    return $(routeuser $tmp "$domain")
                    ;;
            tfiss
    }       # end of rrouter

    routes_spec (domain, address, A) {
            local tmp channel rscshost

            sift "$domain" in
    #       (bsmtp3nd|bsmtp3|bitnet2|bitnet2deliver2)!(.)!(.)
            (bsmtp3nd|bsmtp3|bsmtp3nd|bsmtp3rfc|bsmtp3ndrfc)!(.)!(.)
                    return (((\1 "\2@\3" "$address" $A))) ;;
            (defrt1)!(.)
                    channel=\1
                    rscshost=\2

                    tmp="$(uucproute "$address")"
                    sift "$tmp" in
                    .+!([^!]+)!([^!]+)

We are trying to gateway through a DEFRT1 domain(!)

                            #return (((error bitnetgw "$address" $A))) ;;

This will usually work anyway, sigh...

                            return (((bsmtp3 "mailer@$rscshost" "\2@\1" $A))) ;;
                    ([^!]+)!([^!]+)

The destination domain is the next hop, so we're all happy.

                            return ((($channel "\2@$rscshost" "\2@\1" $A))) ;;
                    tfis
                    ;;
            ignore!.*
                    break
                    ;;
            smtp!
                    ssift "$address" in
                    (.*)@(.+)
                            return (((smtp "\2" "$address" $A)))
                            ;;
                    tfiss
                    ;;
            dns!
                    ssift "$address" in
                    (.*)@(.+)
                            return (((smtp "\2" "$address" $A)))
                            ;;
                    tfiss
                    ;;
            (.?)!
                    return ((("\1" - "$address" $A)))
                    ;;
            delay!(.)

NB! envelope info must also be defined in interactive mode.

                    tmp="$(/bin/expr $(get envelopeinfo now) + "\1")"
                    return (((hold "$tmp" "$address" $A))) ;;
            (.?)!([^!]+)
                    return ((("\1" "\2" "$address" $A))) ;;
            (.?)!(.+)
                    # BEWARE LOOPS
                    return $(rrouter "\2!$(uucproute "$address")" "$address" $A "" "$domain")
                    ;;
            tfis
            return 1
    }

    uucproute (address) {

This function turns any address into a pure-! form. It should not call any other functions, since random other functions call it. In particular it should not use rfc822route which itself uses uucproute.

            sift "$address" in
            (.*)<(.*)>(.*)          address=\1\2\3 ;;               # defocus
            (.+!)@(.+)              address=\1$(uucproute "@\2") ;;
            (.+)([,:]@)(.+)         address=\1!\3 ; continue ;;
            :include:[^!]+          return $address ;;
            @(.+:)([^:]+)           address=\1$(uucproute "\2") ;;
            (.+):(.+)               address=\1!\2 ; continue ;;

This won't work properly for e.g. utzoo!bar@gpu.utcs.toronto.edu because gpu.utcs also has an active uucp connection with utzoo. It will work properly in other cases though, so if we have to guess...

            #([^!])!(.+)@(.+)       if [ $(ldotsys \1) ]; then
            #                               address=\1!\3!\2
            #                       else
            #                               address=\3!\1!\2
            #                       fi ;;
            (.+)!([^!]+)%([^!%]+)@(.+)      # route!a%b@c -> route!c!a@b
                                    address=\1!\4!\2@\3 ; continue ;;
            ([^@]+)@(.+)            address=\2!\1 ;;
            @(.+)                   address=\1 ;;
            (.+)!([^!]+)[%@](.+)    address=\1!\3!\2 ;;
            tfis
            return "$address"
    }       # end of uucproute

Prev | Home | Next
Appendices | Up | Using ZMailer with Mailinglist Managers